diff --git a/bower.json b/bower.json index c222c3c7..311e7f75 100644 --- a/bower.json +++ b/bower.json @@ -6,10 +6,10 @@ "./morris.css" ], "dependencies": { - "jquery": ">= 1.7.0", "raphael": ">= 2.0" }, "devDependencies": { + "jquery": ">= 1.7.0", "mocha": "~1.17.1", "chai": "~1.9.0", "chai-jquery": "~1.2.1", diff --git a/lib/morris.area.coffee b/lib/morris.area.coffee index 86f8595a..da508323 100644 --- a/lib/morris.area.coffee +++ b/lib/morris.area.coffee @@ -7,7 +7,7 @@ class Morris.Area extends Morris.Line constructor: (options) -> return new Morris.Area(options) unless (@ instanceof Morris.Area) - areaOptions = $.extend {}, areaDefaults, options + areaOptions = Morris.extend {}, areaDefaults, options @cumulative = not areaOptions.behaveLikeLine diff --git a/lib/morris.bar.coffee b/lib/morris.bar.coffee index 4c312978..4e878286 100644 --- a/lib/morris.bar.coffee +++ b/lib/morris.bar.coffee @@ -1,7 +1,7 @@ class Morris.Bar extends Morris.Grid constructor: (options) -> return new Morris.Bar(options) unless (@ instanceof Morris.Bar) - super($.extend {}, options, parseTime: false) + super(Morris.extend {}, options, parseTime: false) init: -> @cumulative = @options.stacked @@ -91,15 +91,15 @@ class Morris.Bar extends Morris.Grid Math.cos(angle * Math.PI / 180.0) label.transform("t#{offset},0...") - + {width, height} = Morris.dimensions @el if not @options.horizontal startPos = labelBox.x size = labelBox.width - maxSize = @el.width() + maxSize = width else startPos = labelBox.y size = labelBox.height - maxSize = @el.height() + maxSize = height # try to avoid overlaps if (not prevLabelMargin? or diff --git a/lib/morris.coffee b/lib/morris.coffee index f2cd2df8..afc12ae7 100644 --- a/lib/morris.coffee +++ b/lib/morris.coffee @@ -1,6 +1,13 @@ Morris = window.Morris = {} -$ = jQuery +# Compute element style +compStyle = (el) -> + if getComputedStyle # standard (includes ie9) + getComputedStyle(el, null) + else if el.currentStyle # IE older + el.currentStyle + else # inline style + el.style # Very simple event-emitter class. # @@ -41,3 +48,47 @@ Morris.commas = (num) -> # @example # Morris.pad2(1) -> '01' Morris.pad2 = (number) -> (if number < 10 then '0' else '') + number + +# Copy all properties from objects in second argument to last onto the first +# object given and return the first object. This should emulate jQuery's +# $.extend(). +# +# @example +# Morris.extend({}, { a:1 }, { b:2 }) -> '{ a:1, b:2 }' +Morris.extend = (object={}, objects...) -> + for properties in objects when properties? + for key, val of properties when properties.hasOwnProperty key + object[key] = val + object + +# Emulate jQuery's $el.offset() (http://youmightnotneedjquery.com/#offset) +Morris.offset = (el) -> + rect = el.getBoundingClientRect() + top: rect.top + document.body.scrollTop, + left: rect.left + document.body.scrollLeft + +# Emulate jQuery's $el.css() (http://youmightnotneedjquery.com/#get_style) +Morris.css = (el, prop) -> compStyle(el)[prop] + +# Emulate jQuery's $el.on() +Morris.on = (el, eventName, fn) -> + if el.addEventListener + el.addEventListener(eventName, fn) + else + el.attachEvent('on'+eventName, fn) + +# Emulate jQuery's $el.width() and $el.height() +Morris.dimensions = (el) -> + style = compStyle el + width: parseInt(style.width), + height: parseInt(style.height) + +# Emulate jQuery's $el.innerWidth() and $el.innerHeight() +Morris.innerDimensions = (el) -> + style = compStyle el + width: parseInt(style.width) + + parseInt(style.paddingLeft) + + parseInt(style.paddingRight), + height: parseInt(style.height) + + parseInt(style.paddingTop) + + parseInt(style.paddingBottom) diff --git a/lib/morris.donut.coffee b/lib/morris.donut.coffee index e40314d9..e9c4c9ac 100644 --- a/lib/morris.donut.coffee +++ b/lib/morris.donut.coffee @@ -31,21 +31,21 @@ class Morris.Donut extends Morris.EventEmitter # constructor: (options) -> return new Morris.Donut(options) unless (@ instanceof Morris.Donut) - @options = $.extend {}, @defaults, options + @options = Morris.extend {}, @defaults, options if typeof options.element is 'string' - @el = $ document.getElementById(options.element) + @el = document.getElementById(options.element) else - @el = $ options.element + @el = options.element[0] or options.element - if @el == null || @el.length == 0 + if @el == null throw new Error("Graph placeholder not found.") # bail if there's no data if options.data is undefined or options.data.length is 0 return - @raphael = new Raphael(@el[0]) + @raphael = new Raphael(@el) if @options.resize $(window).bind 'resize', (evt) => @@ -59,8 +59,9 @@ class Morris.Donut extends Morris.EventEmitter redraw: -> @raphael.clear() - cx = @el.width() / 2 - cy = @el.height() / 2 + {width, height} = Morris.dimensions @el + cx = width / 2 + cy = height / 2 w = (Math.min(cx, cy) - 10) / 3 total = 0 @@ -117,7 +118,8 @@ class Morris.Donut extends Morris.EventEmitter # @private setLabels: (label1, label2) -> - inner = (Math.min(@el.width() / 2, @el.height() / 2) - 10) * 2 / 3 + {width, height} = Morris.dimensions(@el) + inner = (Math.min(width / 2, height / 2) - 10) * 2 / 3 maxWidth = 1.8 * inner maxHeightTop = inner / 2 maxHeightBottom = inner / 3 @@ -139,7 +141,8 @@ class Morris.Donut extends Morris.EventEmitter resizeHandler: => @timeoutId = null - @raphael.setSize @el.width(), @el.height() + {width, height} = Morris.dimensions @el + @raphael.setSize width, height @redraw() diff --git a/lib/morris.grid.coffee b/lib/morris.grid.coffee index 0a27623c..e142e78a 100644 --- a/lib/morris.grid.coffee +++ b/lib/morris.grid.coffee @@ -6,23 +6,23 @@ class Morris.Grid extends Morris.EventEmitter constructor: (options) -> # find the container to draw the graph in if typeof options.element is 'string' - @el = $ document.getElementById(options.element) + @el = document.getElementById(options.element) else - @el = $ options.element - if not @el? or @el.length == 0 + @el = options.element[0] or options.element + if not @el? throw new Error("Graph container element not found") - if @el.css('position') == 'static' - @el.css('position', 'relative') + if Morris.css(@el, 'position') == 'static' + @el.style.position = 'relative' - @options = $.extend {}, @gridDefaults, (@defaults || {}), options + @options = Morris.extend {}, @gridDefaults, (@defaults || {}), options # backwards compatibility for units -> postUnits if typeof @options.units is 'string' @options.postUnits = options.units # the raphael drawing instance - @raphael = new Raphael(@el[0]) + @raphael = new Raphael(@el) # some redraw stuff @elementWidth = null @@ -39,8 +39,8 @@ class Morris.Grid extends Morris.EventEmitter @setData @options.data # hover - @el.bind 'mousemove', (evt) => - offset = @el.offset() + Morris.on @el, 'mousemove', (evt) => + offset = Morris.offset(@el) x = evt.pageX - offset.left if @selectFrom left = @data[@hitTest(Math.min(x, @selectFrom))]._x @@ -50,44 +50,44 @@ class Morris.Grid extends Morris.EventEmitter else @fire 'hovermove', x, evt.pageY - offset.top - @el.bind 'mouseleave', (evt) => + Morris.on @el, 'mouseleave', (evt) => if @selectFrom @selectionRect.hide() @selectFrom = null @fire 'hoverout' - @el.bind 'touchstart touchmove touchend', (evt) => + Morris.on @el, 'touchstart touchmove touchend', (evt) => touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0] - offset = @el.offset() + offset = Morris.offset(@el) @fire 'hovermove', touch.pageX - offset.left, touch.pageY - offset.top - @el.bind 'click', (evt) => - offset = @el.offset() + Morris.on @el, 'click', (evt) => + offset = Morris.offset(@el) @fire 'gridclick', evt.pageX - offset.left, evt.pageY - offset.top if @options.rangeSelect - @selectionRect = @raphael.rect(0, 0, 0, @el.innerHeight()) + @selectionRect = @raphael.rect(0, 0, 0, Morris.innerDimensions(@el).height) .attr({ fill: @options.rangeSelectColor, stroke: false }) .toBack() .hide() - @el.bind 'mousedown', (evt) => - offset = @el.offset() + Morris.on @el, 'mousedown', (evt) => + offset = Morris.offset(@el) @startRange evt.pageX - offset.left - @el.bind 'mouseup', (evt) => - offset = @el.offset() + Morris.on @el, 'mouseup', (evt) => + offset = Morris.offset(@el) @endRange evt.pageX - offset.left @fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top if @options.resize - $(window).bind 'resize', (evt) => + Morris.on window, 'resize', (evt) => if @timeoutId? window.clearTimeout @timeoutId @timeoutId = window.setTimeout @resizeHandler, 100 # Disable tap highlight on iOS. - @el.css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)') + @el.style.webkitTapHighlightColor = 'rgba(0,0,0,0)' @postInit() if @postInit @@ -271,8 +271,7 @@ class Morris.Grid extends Morris.EventEmitter grid _calc: -> - w = @el.width() - h = @el.height() + {width:w, height:h} = Morris.dimensions @el if @elementWidth != w or @elementHeight != h or @dirty @elementWidth = w @@ -482,7 +481,8 @@ class Morris.Grid extends Morris.EventEmitter resizeHandler: => @timeoutId = null - @raphael.setSize @el.width(), @el.height() + {width, height} = Morris.dimensions @el + @raphael.setSize width, height @redraw() hasToShow: (i) => diff --git a/lib/morris.hover.coffee b/lib/morris.hover.coffee index fe04178e..7993ed8f 100644 --- a/lib/morris.hover.coffee +++ b/lib/morris.hover.coffee @@ -5,10 +5,11 @@ class Morris.Hover class: 'morris-hover morris-default-style' constructor: (options = {}) -> - @options = $.extend {}, Morris.Hover.defaults, options - @el = $ "
" - @el.hide() - @options.parent.append(@el) + @options = Morris.extend {}, Morris.Hover.defaults, options + @el = document.createElement 'div' + @el.className = @options.class + @el.style.display = 'none' + (@options.parent = @options.parent[0] or @options.parent).appendChild @el update: (html, x, y, centre_y) -> if not html @@ -19,13 +20,13 @@ class Morris.Hover @moveTo(x, y, centre_y) html: (content) -> - @el.html(content) + @el.innerHTML = content moveTo: (x, y, centre_y) -> - parentWidth = @options.parent.innerWidth() - parentHeight = @options.parent.innerHeight() - hoverWidth = @el.outerWidth() - hoverHeight = @el.outerHeight() + {width:parentWidth, height:parentHeight} = + Morris.innerDimensions @options.parent + hoverWidth = @el.offsetWidth + hoverHeight = @el.offsetHeight left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth) if y? if centre_y is true @@ -40,10 +41,11 @@ class Morris.Hover top = parentHeight / 2 - hoverHeight / 2 else top = parentHeight / 2 - hoverHeight / 2 - @el.css(left: left + "px", top: parseInt(top) + "px") + @el.style.left = parseInt(left) + "px" + @el.style.top = parseInt(top) + "px" show: -> - @el.show() + @el.style.display = '' hide: -> - @el.hide() + @el.style.display = 'none' diff --git a/lib/morris.line.coffee b/lib/morris.line.coffee index 3a725670..d182ed1b 100644 --- a/lib/morris.line.coffee +++ b/lib/morris.line.coffee @@ -167,7 +167,8 @@ class Morris.Line extends Morris.Grid if (not prevLabelMargin? or prevLabelMargin >= labelBox.x + labelBox.width or prevAngleMargin? and prevAngleMargin >= labelBox.x) and - labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width() + labelBox.x >= 0 and + (labelBox.x + labelBox.width) < Morris.dimensions(@el).width if @options.xLabelAngle != 0 margin = 1.25 * @options.gridTextSize / Math.sin(@options.xLabelAngle * Math.PI / 180.0) @@ -392,7 +393,7 @@ Morris.labelSeries = (dmin, dmax, pxwidth, specName, xLabelFormat) -> spec = Morris.LABEL_SPECS["second"] # check if there's a user-defined formatting function if xLabelFormat - spec = $.extend({}, spec, {fmt: xLabelFormat}) + spec = Morris.extend({}, spec, {fmt: xLabelFormat}) # calculate labels d = spec.start(d0) ret = [] diff --git a/spec/lib/hover_spec.coffee b/spec/lib/hover_spec.coffee index 868f1a3c..a8a76840 100644 --- a/spec/lib/hover_spec.coffee +++ b/spec/lib/hover_spec.coffee @@ -35,23 +35,23 @@ describe "Morris.Hover", -> it "should place the popup directly above the given point", -> @hover.moveTo(100, 150) - @element.should.have.css('left', '50px') - @element.should.have.css('top', '40px') + getComputedStyle(@element[0])['left'].should.be '50px' + getComputedStyle(@element[0])['top'].should.be '40px' it "should place the popup below the given point if it does not fit above", -> @hover.moveTo(100, 50) - @element.should.have.css('left', '50px') - @element.should.have.css('top', '60px') + getComputedStyle(@element[0])['left'].should.be '50px' + getComputedStyle(@element[0])['top'].should.be '60px' it "should center the popup vertically if it will not fit above or below", -> @hover.moveTo(100, 100) - @element.should.have.css('left', '50px') - @element.should.have.css('top', '40px') + getComputedStyle(@element[0])['left'].should.be '50px' + getComputedStyle(@element[0])['top'].should.be '40px' it "should center the popup vertically if no y value is supplied", -> @hover.moveTo(100) - @element.should.have.css('left', '50px') - @element.should.have.css('top', '40px') + getComputedStyle(@element[0])['left'].should.be '50px' + getComputedStyle(@element[0])['top'].should.be '40px' describe "#update", -> it "should update content, show and reposition the popup", -> @@ -59,6 +59,6 @@ describe "Morris.Hover", -> html = "