diff --git a/build/css/ox.ui.css b/build/css/ox.ui.css
index eca5341a..3b3a2537 100644
--- a/build/css/ox.ui.css
+++ b/build/css/ox.ui.css
@@ -1646,7 +1646,7 @@ Miscellaneous
position: absolute;
padding: 1px 2px 1px 2px;
font-size: 9px;
- opacity: 0;
+ //opacity: 0;
z-index: 12;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
diff --git a/build/js/ox.js b/build/js/ox.js
index 2e9dfbf0..0240a930 100644
--- a/build/js/ox.js
+++ b/build/js/ox.js
@@ -2484,6 +2484,31 @@ Ox.stripTags = function(str) {
return str.replace(/(<.*?>)/gi, '');
};
+Ox.substr = function(str, start, stop) {
+ /***
+ Ox.substr behaves like str[start:stop] in Python
+ (or like str.substring() with negative values for stop)
+ not implemented
+ >>> Ox.substr('foobar', 1)
+ "oobar"
+ >>> Ox.substr('foobar', -1)
+ "r"
+ >>> Ox.substr('foobar', 1, 3)
+ "oo"
+ >>> Ox.substr('foobar', -3, 5)
+ "ba"
+ >>> Ox.substr('foobar', 1, -2)
+ "oob"
+ >>> Ox.substr('foobar', -4, -1)
+ "oba"
+ ***/
+ stop = Ox.isUndefined(stop) ? str.length : stop;
+ return str.substring(
+ start < 0 ? str.length + start : start,
+ stop < 0 ? str.length + stop : stop
+ );
+};
+
Ox.toCamelCase = function(str) {
/*
>>> Ox.toCamelCase("foo-bar-baz")
diff --git a/build/js/ox.ui.js b/build/js/ox.ui.js
index dcc427bb..0e0c8d06 100644
--- a/build/js/ox.ui.js
+++ b/build/js/ox.ui.js
@@ -883,6 +883,13 @@ requires
Basic element object
***/
+ /*
+ tooltip option can be any of the following:
+ string
+ function(e), returns string
+ {mousemove: true, title: function(e)}
+ */
+
return function(options, self) {
if (!(this instanceof arguments.callee)) {
@@ -901,8 +908,19 @@ requires
self.$eventHandler = $('
');
}
- var that = new Ox.$Element($('<' + (self.options.element || 'div') + '>'));
- that.$element.mousedown(mousedown);
+ var that = new Ox.$Element(
+ $('<' + (self.options.element || 'div') + '>')
+ )
+ .mousedown(mousedown);
+
+ /*
+ self.options.tooltip && that.bind(Ox.extend({
+ mouseenter: mouseenter,
+ mouseleave: mouseleave
+ }, self.options.tooltip.mousemove ? {
+ mousemove: mousemove
+ } : {}));
+ */
function mousedown(e) {
/*
@@ -987,6 +1005,26 @@ requires
}
}
+ /*
+ function mouseenter(e) {
+ self.$tooltip = new Ox.Tooltip({
+ title: Ox.isString(self.options.tooltip) ?
+ self.options.tooltip : Ox.isFunction(self.options.tooltip) ?
+ self.options.tooltip(e) : self.options.tooltip.title(e)
+ }).show();
+ }
+
+ function mouseleave(e) {
+ self.$tooltip.hide();
+ }
+
+ function mousemove(e) {
+ self.$tooltip.options({
+ title: self.options.tooltip.title(e)
+ });
+ }
+ */
+
self.onChange = function() {
// self.onChange(key, value)
// is called when an option changes
@@ -10308,20 +10346,21 @@ requires
Ox.MapPlace = function(options) {
- var options = Ox.extend({
- east: 0,
- editing: false,
- geoname: '',
- map: null,
- name: '',
- north: 0,
- selected: false,
- south: 0,
- type: [],
- visible: false,
- west: 0
- }, options),
- that = this;
+ options = Ox.extend({
+ east: 0,
+ editing: false,
+ geoname: '',
+ map: null,
+ name: '',
+ north: 0,
+ selected: false,
+ south: 0,
+ type: [],
+ visible: false,
+ west: 0
+ }, options);
+
+ var that = this;
Ox.forEach(options, function(val, key) {
that[key] = val;
@@ -10865,6 +10904,7 @@ requires
height: self.options.height + 'px'
});
+ self.maxZoom = 28;
self.overlayWidths = [Math.round(self.options.width / 16)];
self.overlayWidths = [
Math.floor((self.options.width - self.overlayWidths[0]) / 2),
@@ -10984,7 +11024,11 @@ requires
top: '24px',
bottom: '40px'
})
- .mousewheel(mousewheel)
+ .bind({
+ mouseleave: mouseleave,
+ mousemove: mousemove,
+ mousewheel: mousewheel
+ })
.bindEvent({
dragstart: dragstart,
drag: drag,
@@ -11041,17 +11085,16 @@ requires
})
.appendTo(that);
- self.$zoombar = new Ox.Bar({
- size: 16
- })
+ self.$zoombar = new Ox.Element()
.css({
position: 'absolute',
- bottom: 24 + 'px'
+ bottom: 24 + 'px',
+ height: '16px'
})
.appendTo(that);
self.$zoomInput = new Ox.Range({
arrows: true,
- max: 28,
+ max: self.maxZoom,
min: 0,
size: self.options.width,
thumbSize: 32,
@@ -11063,6 +11106,24 @@ requires
})
.appendTo(self.$zoombar);
+ self.$statusbar = new Ox.Bar({
+ size: 24
+ })
+ .css({
+ // fixme: no need to set position absolute with map statusbar
+ position: 'absolute',
+ bottom: 0,
+ textAlign: 'center'
+ })
+ .appendTo(that);
+
+ self.$tooltip = new Ox.Tooltip({
+ animate: false
+ })
+ .css({
+ textAlign: 'center'
+ });
+
sortDates();
renderTimelines();
renderDates();
@@ -11096,6 +11157,7 @@ requires
}
function dragpause(event, e) {
+ return;
if (self.drag) {
Ox.print('dragpause')
dragafter();
@@ -11127,6 +11189,7 @@ requires
}
function dragpauseScrollbar(event, e) {
+ return;
self.drag = {x: e.clientX};
dragafter();
}
@@ -11149,6 +11212,50 @@ requires
$('.OxDate').remove();
renderTimelines();
renderDates();
+ var calendarDate = getCalendarDate();
+ self.$statusbar.html(
+ calendarDate.start + ' | ' + self.options.date + ' | ' + calendarDate.stop
+ );
+ }
+
+ function formatDate(date) {
+ var isFullDays = Ox.formatDate(date.start, '%H:%M:%S') == '00:00:00' &&
+ Ox.formatDate(date.stop, '%H:%M:%S') == '00:00:00',
+ isOneDay = isFullDays && date.stop - date.start == 86400000, // fixme: wrong, DST
+ isSameDay = Ox.formatDate(date.start, '%Y-%m-%d') ==
+ Ox.formatDate(date.stop, '%Y-%m-%d'),
+ isSameYear = date.start.getFullYear() == date.stop.getFullYear(),
+ timeFormat = isFullDays ? '' : ', %H:%M:%S',
+ str = Ox.formatDate(date.start, '%a, %b %e');
+ if (isOneDay || isSameDay || !isSameYear) {
+ str += Ox.formatDate(date.start, ', %Y' + timeFormat);
+ }
+ if (!isOneDay && !isSameDay) {
+ str += Ox.formatDate(date.stop, ' - %a, %b %e, %Y' + timeFormat);
+ }
+ if (isSameDay) {
+ str += Ox.formatDate(date.stop, ' - ' + timeFormat.replace(', ', ''));
+ }
+ return str;
+ }
+
+ function getCalendarDate() {
+ var ms = self.options.width * getSecondsPerPixel() * 1000;
+ return {
+ start: new Date(+self.options.date - ms / 2),
+ stop: new Date(+self.options.date + ms / 2)
+ };
+ }
+
+ function getDateByName(name) {
+ var date = {};
+ Ox.forEach(self.options.dates, function(v) {
+ if (v.name == name) {
+ date = v;
+ return false;
+ }
+ });
+ return date;
}
function getDateElement(date, zoom) {
@@ -11156,24 +11263,26 @@ requires
width = Math.max(getPosition(date.stop, zoom) - left, 1);
return new Ox.Element()
.addClass('OxDate')
- .attr({
- title: date.name
- })
.css({
left: left + 'px',
width: width + 'px'
})
+ .data({
+ name: date.name
+ })
.html(' ' + date.name);
}
+ function getMouseDate(e) {
+ return new Date(+self.options.date + (
+ e.clientX - that.offset().left - self.options.width / 2 - 1
+ ) * getSecondsPerPixel() * 1000);
+ }
+
function getPixelsPerSecond(zoom) {
return Math.pow(2, (zoom || self.options.zoom) - 24);
}
- function getSecondsPerPixel(zoom) {
- return 1 / getPixelsPerSecond(zoom);
- }
-
function getPosition(date, zoom) {
zoom = zoom || self.options.zoom
return Math.round(
@@ -11182,6 +11291,10 @@ requires
);
}
+ function getSecondsPerPixel(zoom) {
+ return 1 / getPixelsPerSecond(zoom);
+ }
+
function getTimelineElements(zoom) {
var $elements = [],
pixelsPerSecond = getPixelsPerSecond(zoom),
@@ -11210,10 +11323,36 @@ requires
return $elements;
}
- function mousewheel(event, delta, deltaX, deltaY) {
+ function mouseleave() {
+ self.$tooltip.hide();
+ }
+
+ function mousemove(e) {
+ var $target = $(e.target),
+ date, title;
+ if ($target.is('.OxLine > .OxDate')) {
+ date = getDateByName($target.data('name'));
+ title = '' + date.name + '
' +
+ formatDate(date);
+ } else {
+ title = Ox.formatDate(getMouseDate(e), '%a, %b %e, %Y, %H:%M:%S');
+ }
+ self.$tooltip.options({
+ title: title
+ })
+ .show(e.clientX, e.clientY);
+ }
+
+ function mousewheel(e, delta, deltaX, deltaY) {
Ox.print('mousewheel', delta, deltaX, deltaY);
if (!self.mousewheel && deltaY && Math.abs(deltaY) > Math.abs(deltaX)) {
- self.options.zoom += deltaY < 0 ? -1 : 1;
+ self.options.date = deltaY < 0 ?
+ new Date(2 * +self.options.date - +getMouseDate(e)) :
+ new Date((+self.options.date + +getMouseDate(e)) / 2)
+ self.options.zoom = Ox.limit(self.options.zoom + (
+ deltaY < 0 ? -1 : 1
+ ), 0, self.maxZoom);
+ self.$zoomInput.options({value: self.options.zoom});
$('.OxDate').remove();
renderTimelines();
renderDates();
@@ -11294,6 +11433,32 @@ requires
};
+ Ox.CalendarDate = function(options) {
+
+ var self = {},
+ that = this;
+
+ ['start', 'stop'].forEach(function(v) {
+ var date = self.options[v];
+ if (Ox.isString(date)) {
+ date = new Date(self.options[v]);
+ }
+ });
+
+ self.duration = self.options.stop - self.options.start;
+
+ that.format = function() {
+
+ };
+
+ that.formatDuration = function() {
+
+ };
+
+ return that;
+
+ };
+
/*
============================================================================
Menus
@@ -13024,6 +13189,11 @@ requires
$markerPoint: [],
$selection: [],
$subtitles: [],
+ $tooltip: new Ox.Tooltip({
+ animate: false
+ }).css({
+ textAlign: 'center'
+ }),
hasSubtitles: self.options.subtitles.length,
height: 16,
lines: Math.ceil(self.options.duration / self.options.width),
@@ -13245,7 +13415,7 @@ requires
}
function mouseleave(e) {
- self.$tooltip && self.$tooltip.hide();
+ self.$tooltip.hide();
}
function mousemove(e) {
@@ -13259,19 +13429,16 @@ requires
) {
position = getPosition(e),
subtitle = getSubtitle(position);
- self.$tooltip = new Ox.Tooltip({
+ self.$tooltip.options({
title: subtitle ?
'' +
Ox.highlight(subtitle.value, self.options.find).replace(/\n/g, '
') + '
' +
Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) :
Ox.formatDuration(position, 3)
})
- .css({
- textAlign: 'center'
- })
.show(e.clientX, e.clientY);
} else {
- self.$tooltip && self.$tooltip.hide();
+ self.$tooltip.hide();
}
}
@@ -13482,7 +13649,9 @@ requires
$markerPoint: [],
$subtitles: [],
$tiles: {},
- $tooltip: new Ox.Tooltip(),
+ $tooltip: new Ox.Tooltip({
+ animate: false
+ }),
center: parseInt(self.options.width / 2),
element: that.$element[0],
fps: 25,
@@ -13543,8 +13712,7 @@ requires
function click(event, e) {
self.options.position = Ox.limit(
- self.options.position + (e.clientX - that.$element.offset().left - self.center - 1) / self.fps,
- 0, self.options.duration
+ getPosition(e), 0, self.options.duration
);
setPosition();
triggerChangeEvent();
@@ -13564,6 +13732,10 @@ requires
triggerChangeEvent();
}
+ function getPosition(e) {
+ return self.options.position + (e.clientX - that.offset().left - self.center - 1) / self.fps
+ }
+
function mouseleave(e) {
self.clientX = 0;
self.clientY = 0;
@@ -13632,8 +13804,7 @@ requires
}
function updateTooltip() {
- // fixme: duplicated, need getPosition(e)
- var position = self.options.position + (self.clientX - that.offset().left - self.center - 1) / self.fps;
+ var position = getPosition(self);
if (position >= 0 && position <= self.options.duration) {
self.$tooltip
.options({
@@ -13689,6 +13860,11 @@ requires
$images: [],
$markerPoint: [],
$subtitles: [],
+ $tooltip: new Ox.Tooltip({
+ animate: false
+ }).css({
+ textAlign: 'center'
+ }),
hasSubtitles: self.options.subtitles.length,
height: 16,
margin: 8
@@ -13770,7 +13946,7 @@ requires
}
function mouseleave(e) {
- self.$tooltip && self.$tooltip.hide();
+ self.$tooltip.hide();
}
function mousemove(e) {
@@ -13783,19 +13959,16 @@ requires
) {
position = getPosition(e),
subtitle = getSubtitle(position);
- self.$tooltip = new Ox.Tooltip({
+ self.$tooltip.options({
title: subtitle ?
'' +
Ox.highlight(subtitle.value, self.options.find).replace(/\n/g, '
') + '
' +
Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) :
Ox.formatDuration(position, 3)
})
- .css({
- textAlign: 'center'
- })
.show(e.clientX, e.clientY);
} else {
- self.$tooltip && self.$tooltip.hide();
+ self.$tooltip.hide();
}
}
@@ -15207,7 +15380,7 @@ requires
change: changeSmallTimeline
})
};
- self.$timeline.forEach(function($timeline) {
+ Ox.forEach(self.$timeline, function($timeline) {
$timeline.appendTo(self.$timelines);
});
@@ -15440,19 +15613,22 @@ requires
============================================================================
*/
- /**
- */
Ox.Tooltip = function(options, self) {
var self = self || {},
that = new Ox.Element('div', self)
.defaults({
+ animate: true,
title: ''
})
.options(options || {})
.addClass('OxTooltip')
.html(self.options.title);
+ self.options.animate && that.css({
+ opacity: 0
+ });
+
self.onChange = function(key, value) {
if (key == 'title') {
that.html(value);
@@ -15460,29 +15636,33 @@ requires
};
that.hide = function() {
- that.animate({
- opacity: 0
- }, 0, function() {
+ if (self.options.animate) {
+ that.animate({
+ opacity: 0
+ }, 250, function() {
+ that.removeElement();
+ });
+ } else {
that.removeElement();
- });
+ }
return that;
};
that.show = function(x, y) {
var left, top, width, height;
- $('.OxTooltip').remove(); // fixme: don't use dom
+ $('.OxTooltip').remove(); // fixme: don't use DOM
that.appendTo(Ox.UI.$body);
width = that.width();
height = that.height();
left = Ox.limit(x - width / 2, 0, Ox.UI.$document.width() - width);
top = y > Ox.UI.$document.height() - height - 16 ? y - 32 : y + 16;
that.css({
- left: left + 'px',
- top: top + 'px'
- })
- .animate({
- opacity: 1
- }, 0);
+ left: left + 'px',
+ top: top + 'px'
+ });
+ self.options.animate && that.animate({
+ opacity: 1
+ }, 250);
return that;
};
diff --git a/demos/calendar/js/calendar.js b/demos/calendar/js/calendar.js
index ad377504..d644c9b5 100644
--- a/demos/calendar/js/calendar.js
+++ b/demos/calendar/js/calendar.js
@@ -16,7 +16,7 @@ $(function() {
{name: 'American Civil War', start: new Date('1861-04-12'), stop: new Date('1865-04-10')},
{name: 'Franco-Prussian War', start: new Date('1870-07-19'), stop: new Date('1871-05-11')},
{name: 'Paris Commune', start: new Date('1871-03-18'), stop: new Date('1871-05-29')},
- {name: '20th century', start: new Date('1900-01-01'), stop: new Date('2000-01-01')},
+ {name: '20th century', start: new Date('1900'), stop: new Date('2000')},
{name: 'World War One', start: new Date('1914-07-28'), stop: new Date('1918-11-12')},
{name: 'Russian Revolution', start: new Date('1917'), stop: new Date('1918')},
{name: 'October Revolution', start: new Date('1917-11-07'), stop: new Date('1917-11-09')},