From 7b26ee23c9f304e53cdedae8858f3f698d36031e Mon Sep 17 00:00:00 2001 From: John Bintz Date: Sun, 26 Oct 2014 16:19:17 -0400 Subject: [PATCH] initial commit --- .gitignore | 3 + .npmignore | 2 + Gruntfile.coffee | 46 +++++ README.md | 0 bower.json | 16 ++ karma.conf.js | 73 +++++++ package.json | 36 ++++ src/bismarck.coffee | 15 ++ src/bismarck/animate.coffee | 77 +++++++ src/bismarck/canvas.coffee | 32 +++ src/bismarck/drawable.coffee | 327 ++++++++++++++++++++++++++++++ src/bismarck/layout.coffee | 46 +++++ src/bismarck/resources.coffee | 26 +++ test/bismarck/animateSpec.coffee | 30 +++ test/bismarck/drawableSpec.coffee | 189 +++++++++++++++++ test/bismarck/layoutSpec.coffee | 28 +++ test/bismarckSpec.coffee | 0 test/index.coffee | 17 ++ 18 files changed, 963 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 Gruntfile.coffee create mode 100644 README.md create mode 100644 bower.json create mode 100644 karma.conf.js create mode 100644 package.json create mode 100644 src/bismarck.coffee create mode 100644 src/bismarck/animate.coffee create mode 100644 src/bismarck/canvas.coffee create mode 100644 src/bismarck/drawable.coffee create mode 100644 src/bismarck/layout.coffee create mode 100644 src/bismarck/resources.coffee create mode 100644 test/bismarck/animateSpec.coffee create mode 100644 test/bismarck/drawableSpec.coffee create mode 100644 test/bismarck/layoutSpec.coffee create mode 100644 test/bismarckSpec.coffee create mode 100644 test/index.coffee 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') +