commit 7b26ee23c9f304e53cdedae8858f3f698d36031e Author: John Bintz Date: Sun Oct 26 16:19:17 2014 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69a1da6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/bower_components/ +/node_modules/ + diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..30b9507 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +dist/ + diff --git a/Gruntfile.coffee b/Gruntfile.coffee new file mode 100644 index 0000000..c2a5e0f --- /dev/null +++ b/Gruntfile.coffee @@ -0,0 +1,46 @@ +module.exports = (grunt) -> + require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks) + + grunt.initConfig { + watch: + coffee: + files: ['src/**/*.coffee'] + tasks: ['browserify', 'coffee', 'uglify'] + browserify: + dist: + files: + 'dist/bismarck.js': 'src/bismarck.coffee' + options: + transform: ['coffeeify'] + browserifyOptions: + debug: true + extensions: ['.coffee', '.litcoffee'] + coffee: + lib: + expand: true + cwd: 'src' + src: ['**/*.coffee'] + dest: 'lib' + ext: '.js' + options: + bare: true + karma: + unit: + configFile: 'karma.conf.js' + autoWatch: true + uglify: + options: + mangle: false + dist: + files: + 'dist/bismarck.min.js': 'dist/bismarck.js' + concurrent: + default: + tasks: ['karma', 'watch'] + options: + logConcurrentOutput: true + limit: 2 + } + + grunt.registerTask 'default', ['browserify', 'uglify', 'coffee', 'concurrent:default'] + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..87c1c7b --- /dev/null +++ b/bower.json @@ -0,0 +1,16 @@ +{ + "name": "bismarck", + "version": "0.0.0", + "main": "dist/bismarck.js", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "snap.svg": "~0.3.0", + "jquery": "~2.1.1" + } +} diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..6d94d56 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,73 @@ +// Karma configuration +// Generated on Tue Sep 23 2014 07:24:39 GMT-0400 (EDT) + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + preprocessors: { + 'test/index.coffee': 'browserify', + 'src/coffee/app.coffee': 'browserify', + }, + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['browserify', 'jasmine'], + + // list of files / patterns to load in the browser + files: [ + 'bower_components/jquery/dist/jquery.js', + 'bower_components/snap.svg/dist/snap.svg.js', + 'test/index.coffee' + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + browserify: { + extensions: ['.coffee', '.litcoffee'], + transform: ['coffeeify'], + watch: true, + debug: true + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + + // web server port + port: 9877, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..a46e654 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "bismarck", + "version": "0.0.0", + "description": "", + "main": "lib/bismarck.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "browserify": "^6.1.0", + "coffeeify": "^0.7.0", + "grunt": "^0.4.5", + "grunt-browserify": "^3.1.0", + "grunt-concurrent": "^1.0.0", + "grunt-contrib-coffee": "^0.11.1", + "grunt-contrib-concat": "^0.5.0", + "grunt-contrib-uglify": "^0.6.0", + "grunt-contrib-watch": "^0.6.1", + "grunt-karma": "^0.9.0", + "karma": "^0.12.23", + "karma-bro": "^0.8.0", + "karma-chrome-launcher": "^0.1.4", + "karma-coffee-preprocessor": "^0.2.1", + "karma-firefox-launcher": "^0.1.3", + "karma-jasmine": "~0.2.2", + "karma-phantomjs-launcher": "^0.1.4", + "matchdep": "^0.3.0" + }, + "dependencies": { + "q": "^1.0.1", + "underscore": "^1.7.0", + "xml2js": "^0.4.4" + } +} diff --git a/src/bismarck.coffee b/src/bismarck.coffee new file mode 100644 index 0000000..59ea8ac --- /dev/null +++ b/src/bismarck.coffee @@ -0,0 +1,15 @@ +Canvas = require('./bismarck/canvas') + +Bismarck = (element, options) -> + if typeof element == 'string' + new Canvas(document.getElementById(element), options) + else + new Canvas(element, options) + +Bismarck.debug = false + +module.exports = Bismarck + +if window? + window.Bismarck = Bismarck + diff --git a/src/bismarck/animate.coffee b/src/bismarck/animate.coffee new file mode 100644 index 0000000..11a630e --- /dev/null +++ b/src/bismarck/animate.coffee @@ -0,0 +1,77 @@ +Animate = + _animationsEnabled: true + currentTime: -> +new Date + moveFromTo: (object, from, to, distance) -> + dx = to.x - from.x + dy = to.y - from.y + x = from.x + (dx * distance) + y = from.y + (dy * distance) + + object.moveTo(x, y) + +class Animate.Synchronizer + constructor: -> + @animations = [] + @recalcs = [] + @removers = [] + @isRunning = false + @fpsLimit = 30 + + addAnimation: (recalc, animation) -> + @animations.push(animation) + @recalcs.push(recalc) + @removers.push => @removeAnimation(animation) + @run() + + => @removeAnimation(animation) + + removeAnimation: (animation) -> + index = @animations.indexOf(animation) + @animations.splice(index, 1) + @recalcs.splice(index, 1) + @removers.splice(index, 1) + if @animations.length == 0 + @isRunning = false + + run: => + if not @isRunning + @isRunning = true + + window.requestAnimationFrame => + if @animations.length > 0 + startTime = Animate.currentTime() + Animate._animationsDisabled = false + for i in [0..@animations.length - 1] + if @animations[i] + @animations[i](startTime, @removers[i]) + Animate._animationsDisabled = true + r.recalc() for r in @recalcs + endTime = Animate.currentTime() + if @isRunning + calc = Math.max(0, (1 / @fpsLimit - ((endTime - startTime) / 1000)) * 1000) + setTimeout(@run, calc) + else + @isRunning = false + +Animate.synchronizer = new Animate.Synchronizer() + +Animate.once = (recalc, options, code) -> + startTime = Animate.currentTime() + endTime = startTime + options.duration + + Animate.synchronizer.addAnimation recalc, (currentTime, stop) -> + index = Math.min(1, (currentTime - startTime) / options.duration) + result = code(index) + + stop() if not result or currentTime >= endTime + +Animate.loop = (recalc, options, code) -> + startTime = Animate.currentTime() + + Animate.synchronizer.addAnimation recalc, (currentTime, stop) -> + index = Math.min(1, (currentTime - startTime) % options.duration / options.duration) + result = code(index) + + stop() if not result + +module.exports = Animate diff --git a/src/bismarck/canvas.coffee b/src/bismarck/canvas.coffee new file mode 100644 index 0000000..0808c3c --- /dev/null +++ b/src/bismarck/canvas.coffee @@ -0,0 +1,32 @@ +Drawable = require('./drawable') +Resources = require('./resources') +Layout = require('./layout') + +class Canvas + constructor: (@element, options = {}) -> + @snapElement = Snap(@element) + if not @snapElement.attr('bismarck') + if options.id + @snapElement.attr(id: options.id, bismarck: 'canvas') + + fromLayout: (xml, callback) -> + Layout(xml, this, callback) + + create: (options) -> + drawable = new Drawable(this, options) + drawable.attachTo(@snapElement) + drawable + + find: (id) -> + drawable = new Drawable(this) + drawable.useExisting(@snapElement.select("##{id}")) + drawable + + resources: -> + return @_resources if @_resources + + @_resources = new Resources(this) + @_resources.attachTo(@snapElement) + @_resources + +module.exports = Canvas diff --git a/src/bismarck/drawable.coffee b/src/bismarck/drawable.coffee new file mode 100644 index 0000000..eb9432a --- /dev/null +++ b/src/bismarck/drawable.coffee @@ -0,0 +1,327 @@ +Animate = require('./animate') + +class Drawable + constructor: (@canvas, options = {}) -> + defaults = + centerPoint: 'northwest' + position: {x: 0, y: 0} + angle: 0 + scale: 1 + + for own key, value of defaults + switch key + when 'centerPoint' + @setCenterPoint(value || defaults[key]) + else + this[key] = value || defaults[key] + + for own key, value of options + switch key + when 'centerPoint' + @setCenterPoint(value || defaults[key]) + else + this[key] = value || defaults[key] + + @_resources = @canvas.resources() + @_children = [] + + resources: -> @_resources + snapBBox: -> @_scale.getBBox(true) + + create: (options) -> + drawable = new Drawable(@canvas, options) + drawable.attachTo(@_scale) + drawable.parentDrawable = this + @_children.push(drawable) + drawable + + remove: -> + @_translateRotate.remove() + + attachTo: (snapElement) -> + @_translateRotate = snapElement.group().addClass('-translate') + @_scale = @_translateRotate.group().addClass('-scale') + + @_translateRotate.attr(id: @id, bismarck: 'drawable') + @_currentBBox = @snapBBox() + @snapElement = @_translateRotate + + forceSize: (w, h) -> + @_forcedSize.remove() if @_forcedSize + + @_forcedDims = { w: w, h: h } + @_forcedSize = @_translateRotate.rect(0, 0, w, h).attr(fill: 'rgba(0,0,0,0)') + + me = @_forcedSize.node + @_translateRotate.node.insertBefore(me, @_translateRotate.node.childNodes[0]) + + useExisting: (@_translateRotate) -> + throw "Not a Bismarck element!" if not @_translateRotate.attr('bismarck') + childNodes = @_translateRotate.node.childNodes + + @_scale = Snap(childNodes[0]) + if childNodes[1] + @centerPointCross = Snap(childNodes[1]) + + currentNode = @_translateRotate.node.parentNode + while currentNode + switch currentNode.getAttribute('bismarck') + when 'drawable' + @parentDrawable = new Drawable(@canvas, {}) + @parentDrawable.useExisting(Snap(currentNode)) + currentNode = null + when 'canvas' + currentNode = null + else + # stop! + if currentNode.nodeName == 'svg' + currentNode = null + else + currentNode = currentNode.parentNode + + angular.extend(this, @_translateRotate.data('settings')) + + showCenterPoint: -> + @centerPointCross = @_translateRotate.group() + @centerPointCross.line(0, -5, 0, 5).attr(stroke: 'black') + @centerPointCross.line(-5, 0, 5, 0).attr(stroke: 'black') + + @recalc() + + moveTo: (x, y) -> + @position = {x: x, y: y} + @recalc() + + rotateTo: (angle) -> + @angle = angle + @recalc() + + scaleTo: (scale) -> + @scale = scale + @recalc() + + alignTo: (object, how = 'center', args...) -> + switch how + when 'center' + @moveTo(object.position.x, object.position.y, args...) + when 'horizontal' + @moveTo(@position.x, object.position.y, args...) + when 'vertical' + @moveTo(object.position.x, @position.y, args...) + + centerInParent: -> + @_centerInParent = true + @setCenterPoint('center') + @recalc(true) + + recalc: (down = false) -> + if Animate._animationsEnabled + if @_centerInParent + parent = @parentDrawable.getDimensions() + + @position = { x: parent.w / 2, y: parent.h / 2} + + if @oldScale != @scale + matrix = new Snap.Matrix() + matrix.scale(@scale) + @_scale.transform(matrix) + @oldScale = @scale + + offset = @getOffset() + upperLeft = @upperLeft() + + if @centerPointCross? + matrix = new Snap.Matrix() + matrix.translate(offset.x + upperLeft.x, offset.y + upperLeft.y) + @centerPointCross.transform(matrix) + + matrix = new Snap.Matrix() + + matrix.translate(@position.x, @position.y) + matrix.rotate(@angle, 0, 0) + + if not @_forcedSize + matrix.translate(-offset.x - upperLeft.x, -offset.y - upperLeft.y) + + @_translateRotate.transform(matrix) + + if false + @_translateRotate.data('settings', { + centerPoint: @centerPoint + position: @position + angle: @angle + scale: @scale + _currentBBox: @_currentBBox + _centerInParent: @_centerInParent + _forcedDims: @_forcedDims + _children: @_children + }) + + if down + child.recalc(true) for child in @_children + else + @parentDrawable.recalc() if @parentDrawable? + + moveForward: -> + me = @_translateRotate.node + parent = me.parentNode + + l = parent.childNodes.length - 1 + + for i in [0..l] + if parent.childNodes[i] == me and i < l + parent.insertBefore(parent.childNodes[i + 1], me) + + moveBackward: -> + me = @_translateRotate.node + parent = me.parentNode + + for i in [0..parent.childNodes.length] + if parent.childNodes[i] == me and i > 0 + parent.insertBefore(me, parent.childNodes[i - 1]) + + moveToFront: -> + me = @_translateRotate.node + me.parent.appendChild(me) + + moveToBack: -> + me = @_translateRotate.node + me.parent.insertBefore(me, me.parent.childNodes[0]) + + on: (event, args...) -> + code = args.pop() + targetSelector = args.shift() + + target = @_translateRotate + if targetSelector? + target = target.select(targetSelector) + + target[event](code) + + draw: (code) -> + code(@_scale) + @recalc() + + @_currentBBox = @snapBBox() + + scaleAndRotateCoords: (coords) -> + pairs = if coords.w? then ['w', 'h'] else ['x', 'y'] + + coords[pairs[0]] *= @scale + coords[pairs[1]] *= @scale + + rad2ang = Math.PI / 180 + + radianAngle = rad2ang * @angle + cos = Math.cos(radianAngle) + sin = Math.sin(radianAngle) + + newW = coords[pairs[0]] * cos + coords[pairs[1]] * sin + newH = coords[pairs[1]] * cos + coords[pairs[0]] * sin + + coords[pairs[0]] = newW + coords[pairs[1]] = newh + + coords + + getOffset: -> + output = {} + + source = if @_children.length > 0 + @getDimensions() + else + @_currentBBox + + for coordPart in @centerPoint + output[coordPart.which] = coordPart.convert(source[coordPart.dim]) + + output + + upperLeft: -> + if @_children.length == 0 + { x: 0, y: 0 } + else + furthestUpperLeft = { x: null, y: null } + for child in @_children + offset = child.getOffset() + offset.x = -offset.x + child.position.x + offset.y = -offset.y + child.position.y + + if !furthestUpperLeft.x + furthestUpperLeft.x = offset.x + furthestUpperLeft.y = offset.y + else + furthestUpperLeft.x = Math.min(furthestUpperLeft.x, offset.x) + furthestUpperLeft.y = Math.min(furthestUpperLeft.y, offset.y) + + furthestUpperLeft + + setCenterPoint: (args...) -> + if args.length == 1 + args = switch args[0] + when 'center' + ['50%', '50%'] + when 'northwest' + [0,0] + + @coordinates = {} + + data = [ + { index: 0, which: 'x', dim: 'w' }, + { index: 1, which: 'y', dim: 'h' } + ] + + coordMap = (datum) -> + datum.value = args[datum.index] + if datum.value.substr? and datum.value.substr(-1) == '%' + percent = Number(datum.value.slice(0, -1)) / 100.0 + + datum.convert = (value) -> value * percent + else + datum.convert = (value) -> datum.value + + datum + + @centerPoint = data.map(coordMap) + + getDimensions: -> + if @_forcedDims + @_forcedDims + else + if @_children.length > 0 + @getDimensionsWithChildren() + else + @_currentBBox + + getDimensionsWithChildren: -> + bbox = + sx: null + sy: null + ex: null + ey: null + + for child in @_children + childBBox = child.getDimensions() + childOffset = child.getOffset() + + childBBox.x = child.position.x - childOffset.x + childBBox.y = child.position.y - childOffset.y + + if bbox.sx == null + bbox.sx = childBBox.x + bbox.sy = childBBox.y + bbox.ex = childBBox.x + childBBox.w + bbox.ey = childBBox.y + childBBox.h + else + bbox.sx = Math.min(bbox.sx, childBBox.x) + bbox.sy = Math.min(bbox.sy, childBBox.y) + bbox.ex = Math.max(bbox.ex, childBBox.x + childBBox.w) + bbox.ey = Math.max(bbox.ey, childBBox.y + childBBox.h) + + { w: bbox.ex - bbox.sx, h: bbox.ey - bbox.sy } + + append: (drawable) -> + @_translateRotate.append(drawable._scaleRotate) + +module.exports = Drawable + diff --git a/src/bismarck/layout.coffee b/src/bismarck/layout.coffee new file mode 100644 index 0000000..107e702 --- /dev/null +++ b/src/bismarck/layout.coffee @@ -0,0 +1,46 @@ +xml2js = require('xml2js') +parseString = require('xml2js').parseString +_ = require('underscore') + +svgObjs = + rect: (options, canvas) -> + rect = canvas.rect(options.x, options.y, options.width, options.height) + rect.attr(_.omit(options, 'x', 'y', 'width', 'height')) + + rect + +oneOrMore = (value, code) -> + if value.push? + for node in value + code(node) + else + code(value) + +dive = (node, drawable, topLevel = null) -> + for own key, value of node + switch key + when 'drawable' + oneOrMore value, (v) -> + childDrawable = drawable.create(v.$) + topLevel ||= childDrawable + dive(v, childDrawable, topLevel) + when '$' + # nothing + else + oneOrMore value, (v) -> + drawable.draw (svg) -> + svgObjs[key](v.$, svg) + dive(v, drawable, topLevel) + + topLevel + +module.exports = (xml, canvas, callback = null) -> + parseString xml, (err, result) -> + if err? + throw err + else + diveResult = dive(result, canvas) + + if callback + callback(diveResult) + diff --git a/src/bismarck/resources.coffee b/src/bismarck/resources.coffee new file mode 100644 index 0000000..362bb2f --- /dev/null +++ b/src/bismarck/resources.coffee @@ -0,0 +1,26 @@ +class Resources + constructor: (@canvas) -> + @resources = {} + @bboxes = {} + + attachTo: (snapElement) -> + @resourceGroup = snapElement.group().attr(display: 'none') + + copyIDsFrom: (snapElement, ids...) -> + for id in ids + node = snapElement.select("##{id}") + node.transform('') + @resourceGroup.append(node) + @resources[id] = node + + clone: (id) -> + @resources[id].use() + + copy: (id) -> + @resources[id].clone() + + bbox: (id) -> + @bboxes[id] ||= @resources[id].getBBox() + +module.exports = Resources + diff --git a/test/bismarck/animateSpec.coffee b/test/bismarck/animateSpec.coffee new file mode 100644 index 0000000..28068f5 --- /dev/null +++ b/test/bismarck/animateSpec.coffee @@ -0,0 +1,30 @@ +Canvas = require('../../src/bismarck/canvas') +Animate = require('../../src/bismarck/animate') + +describe 'Animate', -> + svg = null + canvas = null + + beforeEach -> + $('body').append('') + canvas = new Canvas(document.getElementById('svg'), id: 'canvas') + + afterEach -> + $('svg').remove() + + describe '.moveFromTo', -> + it 'should move an object from one point to another', -> + drawable = canvas.create(id: 'drawable', centerPoint: 'northwest') + + Animate.moveFromTo(drawable, {x: 0, y: 0}, {x: 100, y: 50}, 0) + expect(drawable.position).toEqual({x: 0, y: 0}) + expect(drawable.snapElement.getBBox().x).toEqual(0) + + Animate.moveFromTo(drawable, {x: 0, y: 0}, {x: 100, y: 50}, 0.5) + expect(drawable.position).toEqual({x: 50, y: 25}) + expect(drawable.snapElement.getBBox().x).toEqual(50) + + Animate.moveFromTo(drawable, {x: 0, y: 0}, {x: 100, y: 50}, 1.0) + expect(drawable.position).toEqual({x: 100, y: 50}) + expect(drawable.snapElement.getBBox().x).toEqual(100) + diff --git a/test/bismarck/drawableSpec.coffee b/test/bismarck/drawableSpec.coffee new file mode 100644 index 0000000..d23d153 --- /dev/null +++ b/test/bismarck/drawableSpec.coffee @@ -0,0 +1,189 @@ +Canvas = require('../../src/bismarck/canvas') + +describe 'Drawable', -> + svg = null + canvas = null + drawable = null + + beforeEach -> + $('body').append('') + canvas = new Canvas(document.getElementById('svg'), id: 'canvas') + drawable = canvas.create(id: 'drawable', centerPoint: 'northwest') + + afterEach -> + $('svg').remove() + + describe 'offset', -> + describe 'with children', -> + it 'should calculate the offset correctly', -> + childOne = drawable.create(id: 'one', centerPoint: 'northwest') + + childOne.draw (surface) -> + surface.rect(0, 0, 10, 1).attr(fill: '#000000') + + childTwo = drawable.create(id: 'two', centerPoint: 'northwest') + + childTwo.draw (surface) -> + surface.rect(0, 0, 1, 10).attr(fill: '#000000') + + expect(drawable.getOffset().x).toEqual(0) + expect(drawable.getOffset().y).toEqual(0) + + drawable.setCenterPoint('50%', '50%') + + expect(drawable.getOffset().x).toEqual(5) + expect(drawable.getOffset().y).toEqual(5) + + childTwo.moveTo(20, 0) + + expect(drawable.getOffset().x).toEqual(10.5) + expect(drawable.getOffset().y).toEqual(5) + + it 'should calculate the offset correctly', -> + drawable.setCenterPoint('center') + childOne = drawable.create(id: 'one', centerPoint: 'center') + + childOne.draw (surface) -> + surface.rect(0, 0, 20, 30).attr(fill: '#000000') + + childOne.moveTo(0, 0) + + childTwo = drawable.create(id: 'two', centerPoint: 'center') + + childTwo.draw (surface) -> + surface.rect(0, 0, 20, 30).attr(fill: '#000000') + + childTwo.moveTo(0, 30) + + expect(drawable.getOffset().x).toEqual(10) + expect(drawable.getOffset().y).toEqual(30) + + it 'should calculate the offset correctly', -> + spyOn(drawable, 'snapBBox').and.callThrough() + + orect = null + + drawable.setCenterPoint(0, 0) + + drawable.draw (surface) -> + orect = surface.rect(0, 0, 20, 10).attr(fill: '#000000') + + expect(drawable.getOffset().x).toEqual(0) + expect(drawable.getOffset().y).toEqual(0) + + drawable.setCenterPoint('50%', '50%') + + expect(drawable.getOffset().x).toEqual(10) + expect(drawable.getOffset().y).toEqual(5) + + drawable.setCenterPoint('-100%', '0%') + + expect(drawable.getOffset().x).toEqual(-20) + expect(drawable.getOffset().y).toEqual(0) + + drawable.draw (surface) -> + orect = surface.rect(0, 0, 20, 10).attr(fill: 'red') + + expect(drawable.getOffset().x).toEqual(-20) + expect(drawable.getOffset().y).toEqual(0) + + drawable.rotateTo(90) + + expect(drawable.getOffset().x).toEqual(-20) + expect(drawable.getOffset().y).toEqual(0) + + drawable.setCenterPoint('center') + + expect(drawable.getOffset().x).toEqual(10) + expect(drawable.getOffset().y).toEqual(5) + + describe 'dimensions', -> + describe 'after drawing', -> + it 'should position the bbox correctly', -> + spyOn(drawable, 'snapBBox').and.callThrough() + + orect = null + + drawable.draw (surface) -> + orect = surface.rect(0, 0, 20, 10).attr(fill: '#000000') + + expect(drawable.getDimensions().w).toEqual(20) + expect(drawable.getDimensions().h).toEqual(10) + + drawable.draw (surface) -> + orect.remove() + surface.rect(10, 10, 20, 10).attr(fill: '#000000') + + expect(drawable.getDimensions().w).toEqual(20) + expect(drawable.getDimensions().h).toEqual(10) + + expect(drawable.snapBBox.calls.count()).toEqual(2) + + drawable.scaleTo(2) + + expect(drawable.getDimensions().w).toEqual(20) + expect(drawable.getDimensions().h).toEqual(10) + + drawable.rotateTo(90) + + expect(drawable.getDimensions().w).toBeCloseTo(20, 2) + expect(drawable.getDimensions().h).toBeCloseTo(10, 2) + + describe 'with children', -> + describe 'northwest', -> + it 'should composite together all the drawables', -> + childOne = drawable.create(id: 'one', centerPoint: 'northwest') + + childOne.draw (surface) -> + surface.rect(0, 0, 10, 1).attr(fill: '#000000') + + childTwo = drawable.create(id: 'two', centerPoint: 'northwest') + + childTwo.draw (surface) -> + surface.rect(0, 0, 1, 10).attr(fill: '#000000') + + expect(drawable.getDimensions().w).toEqual(10) + expect(drawable.getDimensions().h).toEqual(10) + + expect(drawable.upperLeft().x).toBeCloseTo(0, 1) + expect(drawable.upperLeft().y).toBeCloseTo(0, 1) + + expect(childTwo.upperLeft().x).toBeCloseTo(0, 1) + expect(childTwo.upperLeft().y).toBeCloseTo(0, 1) + + childTwo.moveTo(-10, 0) + + expect(drawable.getDimensions().w).toEqual(20) + expect(drawable.getDimensions().h).toEqual(10) + expect(drawable.upperLeft().x).toBeCloseTo(-10, 1) + expect(drawable.upperLeft().y).toBeCloseTo(0, 1) + expect(childTwo.upperLeft().x).toBeCloseTo(0, 1) + expect(childTwo.upperLeft().y).toBeCloseTo(0, 1) + + describe 'center', -> + it 'should composite together all the drawables', -> + drawable.setCenterPoint('center') + childOne = drawable.create(id: 'one', centerPoint: 'center') + + childOne.draw (surface) -> + surface.rect(0, 0, 10, 20).attr(fill: '#000000') + + childTwo = drawable.create(id: 'two', centerPoint: 'center') + + childTwo.draw (surface) -> + surface.rect(0, 0, 10, 20).attr(fill: '#000000') + + expect(drawable.getDimensions().w).toEqual(10) + expect(drawable.getDimensions().h).toEqual(20) + + expect(childTwo.upperLeft().x).toBeCloseTo(0, 1) + expect(childTwo.upperLeft().y).toBeCloseTo(0, 1) + + expect(drawable.upperLeft().x).toBeCloseTo(-5, 1) + expect(drawable.upperLeft().y).toBeCloseTo(-10, 1) + + childTwo.rotateTo(270) + + expect(drawable.getDimensions().w).toBeCloseTo(10, 2) + expect(drawable.getDimensions().h).toBeCloseTo(20, 2) + diff --git a/test/bismarck/layoutSpec.coffee b/test/bismarck/layoutSpec.coffee new file mode 100644 index 0000000..2972731 --- /dev/null +++ b/test/bismarck/layoutSpec.coffee @@ -0,0 +1,28 @@ +Canvas = require('../../src/bismarck/canvas') + +describe 'layoutSpec', -> + svg = null + canvas = null + drawable = null + + beforeEach -> + $('body').append('') + canvas = new Canvas(document.getElementById('svg'), id: 'canvas') + drawable = canvas.create(id: 'drawable', centerPoint: 'northwest') + svg = canvas.snapElement + + afterEach -> + $('svg').remove() + + describe 'from canvas', -> + it 'should create a group of objects with SVG bits', -> + layout = canvas.fromLayout(""" + + + + """) + + console.log svg.outerSVG() + + expect(svg).toContainSVG('#cat') + expect(svg).toContainSVG('rect#dog[style="fill: red"]') diff --git a/test/bismarckSpec.coffee b/test/bismarckSpec.coffee new file mode 100644 index 0000000..e69de29 diff --git a/test/index.coffee b/test/index.coffee new file mode 100644 index 0000000..2cc47d2 --- /dev/null +++ b/test/index.coffee @@ -0,0 +1,17 @@ +beforeEach -> + jasmine.addMatchers { + toContainSVG: -> + compare: (svg, select) -> + result = { pass: svg.select(select)? } + + result.message = if !result.pass + "Expect #{svg.outerSVG()} to contain #{select}" + + result + } + +require('./bismarckSpec') +require('./bismarck/animateSpec') +require('./bismarck/drawableSpec') +require('./bismarck/layoutSpec') +