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')},