holder_rails 2.7.1 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79563e575da1d9d7c264c59a17d32a8915167871
4
- data.tar.gz: a5ddf864af6375f97cc327af3ed3418d172fe567
3
+ metadata.gz: 3ce17582d2875c6a55f69eb0b88df95057f05b0e
4
+ data.tar.gz: 3f98323d52721e5e86f86abb1ed8c6c62012a40e
5
5
  SHA512:
6
- metadata.gz: 2df3996873ccde136425e8c6f662a53ccc604932867e2c55b9b5c0df97d4c9fad26b44967c538511d3b7605a16c25ffa90233e377c56e7b3787057222285c73b
7
- data.tar.gz: 715971f18e1c94b6702112e34c3e3cd7a79e9c906cb49d835f7e317a22d65fba497e4fc9f6c595e3987a70ede1a8f7a052bbff1c219196de272d952d5ee48a59
6
+ metadata.gz: bcb36fed3e0cbd0c46ff569e3d249f08d790da9fd143eddb0189da38f6e7aa48985245a444ff7dcff0cd4147e522ba13d38034fcb5832860372aec055d4e4c4b
7
+ data.tar.gz: ab5cca3f708a29f01abf1309ae402eac2eb12e9ea180af710acdb061a34c46abe9ac1731609b3dd63422404b6dce8742973816d22e2563a101a9a13daad5d3af
@@ -1,3 +1,3 @@
1
1
  module HolderRails
2
- VERSION = "2.7.1"
2
+ VERSION = "2.8.0"
3
3
  end
@@ -1,12 +1,12 @@
1
1
  /*!
2
2
 
3
3
  Holder - client side image placeholders
4
- Version 2.7.1+6hydf
4
+ Version 2.8.0+7srgw
5
5
  © 2015 Ivan Malopinsky - http://imsky.co
6
6
 
7
7
  Site: http://holderjs.com
8
8
  Issues: https://github.com/imsky/holder/issues
9
- License: http://opensource.org/licenses/MIT
9
+ License: MIT
10
10
 
11
11
  */
12
12
  (function (window) {
@@ -283,27 +283,42 @@ return /******/ (function(modules) { // webpackBootstrap
283
283
  /************************************************************************/
284
284
  /******/ ([
285
285
  /* 0 */
286
+ /***/ function(module, exports, __webpack_require__) {
287
+
288
+ /*
289
+ Holder.js - client side image placeholders
290
+ (c) 2012-2015 Ivan Malopinsky - http://imsky.co
291
+ */
292
+
293
+ module.exports = __webpack_require__(1);
294
+
295
+
296
+ /***/ },
297
+ /* 1 */
286
298
  /***/ function(module, exports, __webpack_require__) {
287
299
 
288
300
  /* WEBPACK VAR INJECTION */(function(global) {/*
289
301
  Holder.js - client side image placeholders
290
- © 2012-2015 Ivan Malopinsky - http://imsky.co
302
+ (c) 2012-2015 Ivan Malopinsky - http://imsky.co
291
303
  */
292
304
 
293
305
  //Libraries and functions
294
- var onDomReady = __webpack_require__(1);
295
- var SceneGraph = __webpack_require__(2);
296
- var utils = __webpack_require__(3);
297
- var querystring = __webpack_require__(4);
306
+ var onDomReady = __webpack_require__(3);
307
+ var querystring = __webpack_require__(2);
308
+
309
+ var SceneGraph = __webpack_require__(4);
310
+ var utils = __webpack_require__(5);
311
+ var SVG = __webpack_require__(6);
312
+ var DOM = __webpack_require__(7);
313
+ var Color = __webpack_require__(8);
298
314
 
299
315
  var extend = utils.extend;
300
- var getNodeArray = utils.getNodeArray;
301
316
  var dimensionCheck = utils.dimensionCheck;
302
317
 
303
318
  //Constants and definitions
304
319
  var SVG_NS = 'http://www.w3.org/2000/svg';
305
320
  var NODE_TYPE_COMMENT = 8;
306
- var version = '2.7.1';
321
+ var version = '2.8.0';
307
322
  var generatorComment = '\n' +
308
323
  'Created with Holder.js ' + version + '.\n' +
309
324
  'Learn more at http://holderjs.com\n' +
@@ -328,16 +343,17 @@ return /******/ (function(modules) { // webpackBootstrap
328
343
  * Appends a placeholder to an element
329
344
  *
330
345
  * @param {string} src Placeholder URL string
331
- * @param {string} el Selector of target element(s)
346
+ * @param el A selector or a reference to a DOM node
332
347
  */
333
348
  addImage: function(src, el) {
334
- var node = document.querySelectorAll(el);
349
+ //todo: use jquery fallback if available for all QSA references
350
+ var node = DOM.getNodeArray(el);
335
351
  if (node.length) {
336
352
  for (var i = 0, l = node.length; i < l; i++) {
337
- var img = newEl('img');
353
+ var img = DOM.newEl('img');
338
354
  var domProps = {};
339
- domProps[App.vars.dataAttr] = src;
340
- setAttr(img, domProps);
355
+ domProps[App.setup.dataAttr] = src;
356
+ DOM.setAttr(img, domProps);
341
357
  node[i].appendChild(img);
342
358
  }
343
359
  }
@@ -366,22 +382,24 @@ return /******/ (function(modules) { // webpackBootstrap
366
382
  * @param {Object} userOptions Options object, can contain domain, themes, images, and bgnodes properties
367
383
  */
368
384
  run: function(userOptions) {
385
+ //todo: split processing into separate queues
369
386
  userOptions = userOptions || {};
370
387
  var engineSettings = {};
371
388
  var options = extend(App.settings, userOptions);
372
389
 
373
390
  App.vars.preempted = true;
374
- App.vars.dataAttr = options.dataAttr || App.vars.dataAttr;
391
+ App.vars.dataAttr = options.dataAttr || App.setup.dataAttr;
392
+ App.vars.lineWrapRatio = options.lineWrapRatio || App.setup.lineWrapRatio;
375
393
 
376
394
  engineSettings.renderer = options.renderer ? options.renderer : App.setup.renderer;
377
395
  if (App.setup.renderers.join(',').indexOf(engineSettings.renderer) === -1) {
378
396
  engineSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html');
379
397
  }
380
398
 
381
- var images = getNodeArray(options.images);
382
- var bgnodes = getNodeArray(options.bgnodes);
383
- var stylenodes = getNodeArray(options.stylenodes);
384
- var objects = getNodeArray(options.objects);
399
+ var images = DOM.getNodeArray(options.images);
400
+ var bgnodes = DOM.getNodeArray(options.bgnodes);
401
+ var stylenodes = DOM.getNodeArray(options.stylenodes);
402
+ var objects = DOM.getNodeArray(options.objects);
385
403
 
386
404
  engineSettings.stylesheets = [];
387
405
  engineSettings.svgXMLStylesheet = true;
@@ -392,7 +410,7 @@ return /******/ (function(modules) { // webpackBootstrap
392
410
  if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') {
393
411
  var href = styleNode.attributes.href.value;
394
412
  //todo: write isomorphic relative-to-absolute URL function
395
- var proxyLink = newEl('a');
413
+ var proxyLink = DOM.newEl('a');
396
414
  proxyLink.href = href;
397
415
  var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
398
416
  engineSettings.stylesheets.push(stylesheetURL);
@@ -404,13 +422,7 @@ return /******/ (function(modules) { // webpackBootstrap
404
422
  if (!global.getComputedStyle) continue;
405
423
  var backgroundImage = global.getComputedStyle(bgnodes[i], null).getPropertyValue('background-image');
406
424
  var dataBackgroundImage = bgnodes[i].getAttribute('data-background-src');
407
- var rawURL = null;
408
-
409
- if (dataBackgroundImage == null) {
410
- rawURL = backgroundImage;
411
- } else {
412
- rawURL = dataBackgroundImage;
413
- }
425
+ var rawURL = dataBackgroundImage || backgroundImage;
414
426
 
415
427
  var holderURL = null;
416
428
  var holderString = '?' + options.domain + '/';
@@ -507,7 +519,6 @@ return /******/ (function(modules) { // webpackBootstrap
507
519
  objects: 'object',
508
520
  bgnodes: 'body .holderjs',
509
521
  stylenodes: 'head link.holderjs',
510
- stylesheets: [],
511
522
  themes: {
512
523
  'gray': {
513
524
  background: '#EEEEEE',
@@ -539,69 +550,6 @@ return /******/ (function(modules) { // webpackBootstrap
539
550
  size: 10,
540
551
  units: 'pt',
541
552
  scale: 1 / 16
542
- },
543
- //todo: remove in 2.8
544
- flags: {
545
- dimensions: {
546
- regex: /^(\d+)x(\d+)$/,
547
- output: function(val) {
548
- var exec = this.regex.exec(val);
549
- return {
550
- width: +exec[1],
551
- height: +exec[2]
552
- };
553
- }
554
- },
555
- fluid: {
556
- regex: /^([0-9]+%?)x([0-9]+%?)$/,
557
- output: function(val) {
558
- var exec = this.regex.exec(val);
559
- return {
560
- width: exec[1],
561
- height: exec[2]
562
- };
563
- }
564
- },
565
- colors: {
566
- regex: /(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,
567
- output: function(val) {
568
- var exec = this.regex.exec(val);
569
- return {
570
- foreground: '#' + exec[2],
571
- background: '#' + exec[1]
572
- };
573
- }
574
- },
575
- text: {
576
- regex: /text\:(.*)/,
577
- output: function(val) {
578
- return this.regex.exec(val)[1].replace('\\/', '/');
579
- }
580
- },
581
- font: {
582
- regex: /font\:(.*)/,
583
- output: function(val) {
584
- return this.regex.exec(val)[1];
585
- }
586
- },
587
- auto: {
588
- regex: /^auto$/
589
- },
590
- textmode: {
591
- regex: /textmode\:(.*)/,
592
- output: function(val) {
593
- return this.regex.exec(val)[1];
594
- }
595
- },
596
- random: {
597
- regex: /^random$/
598
- },
599
- size: {
600
- regex: /size\:(\d+)/,
601
- output: function(val) {
602
- return this.regex.exec(val)[1];
603
- }
604
- }
605
553
  }
606
554
  };
607
555
 
@@ -640,11 +588,7 @@ return /******/ (function(modules) { // webpackBootstrap
640
588
  instanceOptions: options
641
589
  };
642
590
 
643
- if (url.match(/([\d]+p?)x([\d]+p?)(?:\?|$)/)) {
644
- return parseQueryString(url, holder);
645
- } else {
646
- return parseFlags(url, holder);
647
- }
591
+ return parseQueryString(url, holder);
648
592
  }
649
593
 
650
594
  /**
@@ -685,6 +629,11 @@ return /******/ (function(modules) { // webpackBootstrap
685
629
  holder.theme.foreground = (options.fg.indexOf('#') === -1 ? '#' : '') + options.fg;
686
630
  }
687
631
 
632
+ //todo: add automatic foreground to themes without foreground
633
+ if (options.bg && !options.fg) {
634
+ holder.autoFg = true;
635
+ }
636
+
688
637
  if (options.theme && holder.instanceOptions.themes.hasOwnProperty(options.theme)) {
689
638
  holder.theme = extend(holder.instanceOptions.themes[options.theme], null);
690
639
  }
@@ -717,6 +666,8 @@ return /******/ (function(modules) { // webpackBootstrap
717
666
 
718
667
  holder.auto = utils.truthy(options.auto);
719
668
 
669
+ holder.outline = utils.truthy(options.outline);
670
+
720
671
  if (utils.truthy(options.random)) {
721
672
  App.vars.cache.themeKeys = App.vars.cache.themeKeys || Object.keys(holder.instanceOptions.themes);
722
673
  var _theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
@@ -727,88 +678,6 @@ return /******/ (function(modules) { // webpackBootstrap
727
678
  return holder;
728
679
  }
729
680
 
730
- //todo: remove in 2.8
731
- /**
732
- * Processes a Holder URL and extracts flags
733
- *
734
- * @private
735
- * @deprecated
736
- * @param url URL
737
- * @param holder Staging Holder object
738
- */
739
- function parseFlags(url, holder) {
740
- var render = false;
741
- var vtab = String.fromCharCode(11);
742
- var flags = url.replace(/([^\\])\//g, '$1' + vtab).split(vtab);
743
- var uriRegex = /%[0-9a-f]{2}/gi;
744
- var options = holder.instanceOptions;
745
-
746
- holder.holderURL = [];
747
-
748
- for (var fl = flags.length, j = 0; j < fl; j++) {
749
- var flag = flags[j];
750
- if (flag.match(uriRegex)) {
751
- try {
752
- flag = decodeURIComponent(flag);
753
- } catch (e) {
754
- flag = flags[j];
755
- }
756
- }
757
-
758
- var push = false;
759
-
760
- if (App.flags.dimensions.match(flag)) {
761
- render = true;
762
- holder.dimensions = App.flags.dimensions.output(flag);
763
- push = true;
764
- } else if (App.flags.fluid.match(flag)) {
765
- render = true;
766
- holder.dimensions = App.flags.fluid.output(flag);
767
- holder.fluid = true;
768
- push = true;
769
- } else if (App.flags.textmode.match(flag)) {
770
- holder.textmode = App.flags.textmode.output(flag);
771
- push = true;
772
- } else if (App.flags.colors.match(flag)) {
773
- var colors = App.flags.colors.output(flag);
774
- holder.theme = extend(holder.theme, colors);
775
- push = true;
776
- } else if (options.themes[flag]) {
777
- //If a theme is specified, it will override custom colors
778
- if (options.themes.hasOwnProperty(flag)) {
779
- holder.theme = extend(options.themes[flag], null);
780
- }
781
- push = true;
782
- } else if (App.flags.font.match(flag)) {
783
- holder.font = App.flags.font.output(flag);
784
- push = true;
785
- } else if (App.flags.auto.match(flag)) {
786
- holder.auto = true;
787
- push = true;
788
- } else if (App.flags.text.match(flag)) {
789
- holder.text = App.flags.text.output(flag);
790
- push = true;
791
- } else if (App.flags.size.match(flag)) {
792
- holder.size = App.flags.size.output(flag);
793
- push = true;
794
- } else if (App.flags.random.match(flag)) {
795
- if (App.vars.cache.themeKeys == null) {
796
- App.vars.cache.themeKeys = Object.keys(options.themes);
797
- }
798
- var theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
799
- holder.theme = extend(options.themes[theme], null);
800
- push = true;
801
- }
802
-
803
- if (push) {
804
- holder.holderURL.push(flag);
805
- }
806
- }
807
- holder.holderURL.unshift(options.domain);
808
- holder.holderURL = holder.holderURL.join('/');
809
- return render ? holder : false;
810
- }
811
-
812
681
  /**
813
682
  * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders)
814
683
  *
@@ -858,14 +727,14 @@ return /******/ (function(modules) { // webpackBootstrap
858
727
 
859
728
  if (mode == 'background') {
860
729
  if (el.getAttribute('data-background-src') == null) {
861
- setAttr(el, {
730
+ DOM.setAttr(el, {
862
731
  'data-background-src': holderURL
863
732
  });
864
733
  }
865
734
  } else {
866
735
  var domProps = {};
867
736
  domProps[App.vars.dataAttr] = holderURL;
868
- setAttr(el, domProps);
737
+ DOM.setAttr(el, domProps);
869
738
  }
870
739
 
871
740
  flags.theme = theme;
@@ -877,7 +746,7 @@ return /******/ (function(modules) { // webpackBootstrap
877
746
  };
878
747
 
879
748
  if (mode == 'image' || mode == 'fluid') {
880
- setAttr(el, {
749
+ DOM.setAttr(el, {
881
750
  'alt': (theme.text ? theme.text + ' [' + dimensionsCaption + ']' : dimensionsCaption)
882
751
  });
883
752
  }
@@ -948,8 +817,8 @@ return /******/ (function(modules) { // webpackBootstrap
948
817
  function render(renderSettings) {
949
818
  var image = null;
950
819
  var mode = renderSettings.mode;
951
- var holderSettings = renderSettings.holderSettings;
952
820
  var el = renderSettings.el;
821
+ var holderSettings = renderSettings.holderSettings;
953
822
  var engineSettings = renderSettings.engineSettings;
954
823
 
955
824
  switch (engineSettings.renderer) {
@@ -1001,14 +870,14 @@ return /******/ (function(modules) { // webpackBootstrap
1001
870
  el.style.backgroundSize = scene.width + 'px ' + scene.height + 'px';
1002
871
  } else {
1003
872
  if (el.nodeName.toLowerCase() === 'img') {
1004
- setAttr(el, {
873
+ DOM.setAttr(el, {
1005
874
  'src': image
1006
875
  });
1007
876
  } else if (el.nodeName.toLowerCase() === 'object') {
1008
- setAttr(el, {
877
+ DOM.setAttr(el, {
1009
878
  'data': image
1010
879
  });
1011
- setAttr(el, {
880
+ DOM.setAttr(el, {
1012
881
  'type': 'image/svg+xml'
1013
882
  });
1014
883
  }
@@ -1020,22 +889,22 @@ return /******/ (function(modules) { // webpackBootstrap
1020
889
  }
1021
890
  //todo: refactor this code into a function
1022
891
  if (el.nodeName.toLowerCase() === 'img') {
1023
- setAttr(el, {
892
+ DOM.setAttr(el, {
1024
893
  'src': image
1025
894
  });
1026
895
  } else if (el.nodeName.toLowerCase() === 'object') {
1027
- setAttr(el, {
896
+ DOM.setAttr(el, {
1028
897
  'data': image
1029
898
  });
1030
- setAttr(el, {
899
+ DOM.setAttr(el, {
1031
900
  'type': 'image/svg+xml'
1032
901
  });
1033
902
  }
1034
- }, 100);
903
+ }, 150);
1035
904
  }
1036
905
  }
1037
906
  //todo: account for re-rendering
1038
- setAttr(el, {
907
+ DOM.setAttr(el, {
1039
908
  'data-holder-rendered': true
1040
909
  });
1041
910
  }
@@ -1093,11 +962,33 @@ return /******/ (function(modules) { // webpackBootstrap
1093
962
  holderBg.resize(scene.width, scene.height);
1094
963
  sceneGraph.root.add(holderBg);
1095
964
 
965
+ if (scene.flags.outline) {
966
+ //todo: generalize darken/lighten to more than RRGGBB hex values
967
+ var outlineColor = new Color(holderBg.properties.fill);
968
+
969
+ outlineColor = outlineColor.lighten(outlineColor.lighterThan('7f7f7f') ? -0.1 : 0.1);
970
+
971
+ holderBg.properties.outline = {
972
+ fill: outlineColor.toHex(true),
973
+ width: 2
974
+ };
975
+ }
976
+
977
+ var holderTextColor = scene.theme.foreground;
978
+
979
+ if (scene.flags.autoFg) {
980
+ var holderBgColor = new Color(holderBg.properties.fill);
981
+ var lightColor = new Color('fff');
982
+ var darkColor = new Color('000', { 'alpha': 0.285714 });
983
+
984
+ holderTextColor = holderBgColor.blendAlpha(holderBgColor.lighterThan('7f7f7f') ? darkColor : lightColor).toHex(true);
985
+ }
986
+
1096
987
  var holderTextGroup = new Shape.Group('holderTextGroup', {
1097
988
  text: scene.text,
1098
989
  align: scene.align,
1099
990
  font: scene.font,
1100
- fill: scene.theme.foreground
991
+ fill: holderTextColor
1101
992
  });
1102
993
 
1103
994
  holderTextGroup.moveTo(null, null, 1);
@@ -1119,7 +1010,7 @@ return /******/ (function(modules) { // webpackBootstrap
1119
1010
  parent.height += line.height;
1120
1011
  }
1121
1012
 
1122
- var sceneMargin = scene.width * App.setup.lineWrapRatio;
1013
+ var sceneMargin = scene.width * App.vars.lineWrapRatio;
1123
1014
  var maxLineWidth = sceneMargin;
1124
1015
 
1125
1016
  if (tpdata.lineCount > 1) {
@@ -1131,7 +1022,7 @@ return /******/ (function(modules) { // webpackBootstrap
1131
1022
 
1132
1023
  //Double margin so that left/right-aligned next is not flush with edge of image
1133
1024
  if (scene.align === 'left' || scene.align === 'right') {
1134
- maxLineWidth = scene.width * (1 - (1 - (App.setup.lineWrapRatio)) * 2);
1025
+ maxLineWidth = scene.width * (1 - (1 - (App.vars.lineWrapRatio)) * 2);
1135
1026
  }
1136
1027
 
1137
1028
  for (var i = 0; i < tpdata.words.length; i++) {
@@ -1200,7 +1091,6 @@ return /******/ (function(modules) { // webpackBootstrap
1200
1091
  }
1201
1092
 
1202
1093
  //todo: renderlist
1203
-
1204
1094
  return sceneGraph;
1205
1095
  }
1206
1096
 
@@ -1384,14 +1274,14 @@ return /******/ (function(modules) { // webpackBootstrap
1384
1274
  firstTimeSetup = true;
1385
1275
  }
1386
1276
 
1387
- svg = initSVG(svg, rootNode.properties.width, rootNode.properties.height);
1277
+ svg = SVG.initSVG(svg, rootNode.properties.width, rootNode.properties.height);
1388
1278
  //Show staging element before staging
1389
1279
  svg.style.display = 'block';
1390
1280
 
1391
1281
  if (firstTimeSetup) {
1392
- stagingText = newEl('text', SVG_NS);
1282
+ stagingText = DOM.newEl('text', SVG_NS);
1393
1283
  stagingTextNode = tnode(null);
1394
- setAttr(stagingText, {
1284
+ DOM.setAttr(stagingText, {
1395
1285
  x: 0
1396
1286
  });
1397
1287
  stagingText.appendChild(stagingTextNode);
@@ -1408,7 +1298,7 @@ return /******/ (function(modules) { // webpackBootstrap
1408
1298
 
1409
1299
  var holderTextGroup = rootNode.children.holderTextGroup;
1410
1300
  var htgProps = holderTextGroup.properties;
1411
- setAttr(stagingText, {
1301
+ DOM.setAttr(stagingText, {
1412
1302
  'y': htgProps.font.size,
1413
1303
  'style': utils.cssProps({
1414
1304
  'font-weight': htgProps.font.weight,
@@ -1422,7 +1312,7 @@ return /******/ (function(modules) { // webpackBootstrap
1422
1312
  var stagingTextBBox = stagingText.getBBox();
1423
1313
 
1424
1314
  //Get line count and split the string into words
1425
- var lineCount = Math.ceil(stagingTextBBox.width / (rootNode.properties.width * App.setup.lineWrapRatio));
1315
+ var lineCount = Math.ceil(stagingTextBBox.width / (rootNode.properties.width * App.vars.lineWrapRatio));
1426
1316
  var words = htgProps.text.split(' ');
1427
1317
  var newlines = htgProps.text.match(/\\n/g);
1428
1318
  lineCount += newlines == null ? 0 : newlines.length;
@@ -1467,7 +1357,7 @@ return /******/ (function(modules) { // webpackBootstrap
1467
1357
  })();
1468
1358
 
1469
1359
  var sgCanvasRenderer = (function() {
1470
- var canvas = newEl('canvas');
1360
+ var canvas = DOM.newEl('canvas');
1471
1361
  var ctx = null;
1472
1362
 
1473
1363
  return function(sceneGraph) {
@@ -1479,8 +1369,33 @@ return /******/ (function(modules) { // webpackBootstrap
1479
1369
  canvas.height = App.dpr(root.properties.height);
1480
1370
  ctx.textBaseline = 'middle';
1481
1371
 
1482
- ctx.fillStyle = root.children.holderBg.properties.fill;
1483
- ctx.fillRect(0, 0, App.dpr(root.children.holderBg.width), App.dpr(root.children.holderBg.height));
1372
+ var bg = root.children.holderBg;
1373
+ var bgWidth = App.dpr(bg.width);
1374
+ var bgHeight = App.dpr(bg.height);
1375
+ //todo: parametrize outline width (e.g. in scene object)
1376
+ var outlineWidth = 2;
1377
+ var outlineOffsetWidth = outlineWidth / 2;
1378
+
1379
+ ctx.fillStyle = bg.properties.fill;
1380
+ ctx.fillRect(0, 0, bgWidth, bgHeight);
1381
+
1382
+ if (bg.properties.outline) {
1383
+ //todo: abstract this into a method
1384
+ ctx.strokeStyle = bg.properties.outline.fill;
1385
+ ctx.lineWidth = bg.properties.outline.width;
1386
+ ctx.moveTo(outlineOffsetWidth, outlineOffsetWidth);
1387
+ // TL, TR, BR, BL
1388
+ ctx.lineTo(bgWidth - outlineOffsetWidth, outlineOffsetWidth);
1389
+ ctx.lineTo(bgWidth - outlineOffsetWidth, bgHeight - outlineOffsetWidth);
1390
+ ctx.lineTo(outlineOffsetWidth, bgHeight - outlineOffsetWidth);
1391
+ ctx.lineTo(outlineOffsetWidth, outlineOffsetWidth);
1392
+ // Diagonals
1393
+ ctx.moveTo(0, outlineOffsetWidth);
1394
+ ctx.lineTo(bgWidth, bgHeight - outlineOffsetWidth);
1395
+ ctx.moveTo(0, bgHeight - outlineOffsetWidth);
1396
+ ctx.lineTo(bgWidth, outlineOffsetWidth);
1397
+ ctx.stroke();
1398
+ }
1484
1399
 
1485
1400
  var textGroup = root.children.holderTextGroup;
1486
1401
  var tgProps = textGroup.properties;
@@ -1505,9 +1420,9 @@ return /******/ (function(modules) { // webpackBootstrap
1505
1420
  var sgSVGRenderer = (function() {
1506
1421
  //Prevent IE <9 from initializing SVG renderer
1507
1422
  if (!global.XMLSerializer) return;
1508
- var xml = createXML();
1509
- var svg = initSVG(null, 0, 0);
1510
- var bgEl = newEl('rect', SVG_NS);
1423
+ var xml = DOM.createXML();
1424
+ var svg = SVG.initSVG(null, 0, 0);
1425
+ var bgEl = DOM.newEl('rect', SVG_NS);
1511
1426
  svg.appendChild(bgEl);
1512
1427
 
1513
1428
  //todo: create a reusable pool for textNodes, resize if more words present
@@ -1515,7 +1430,7 @@ return /******/ (function(modules) { // webpackBootstrap
1515
1430
  return function(sceneGraph, renderSettings) {
1516
1431
  var root = sceneGraph.root;
1517
1432
 
1518
- initSVG(svg, root.properties.width, root.properties.height);
1433
+ SVG.initSVG(svg, root.properties.width, root.properties.height);
1519
1434
 
1520
1435
  var groups = svg.querySelectorAll('g');
1521
1436
 
@@ -1525,10 +1440,10 @@ return /******/ (function(modules) { // webpackBootstrap
1525
1440
 
1526
1441
  var holderURL = renderSettings.holderSettings.flags.holderURL;
1527
1442
  var holderId = 'holder_' + (Number(new Date()) + 32768 + (0 | Math.random() * 32768)).toString(16);
1528
- var sceneGroupEl = newEl('g', SVG_NS);
1443
+ var sceneGroupEl = DOM.newEl('g', SVG_NS);
1529
1444
  var textGroup = root.children.holderTextGroup;
1530
1445
  var tgProps = textGroup.properties;
1531
- var textGroupEl = newEl('g', SVG_NS);
1446
+ var textGroupEl = DOM.newEl('g', SVG_NS);
1532
1447
  var tpdata = textGroup.textPositionData;
1533
1448
  var textCSSRule = '#' + holderId + ' text { ' +
1534
1449
  utils.cssProps({
@@ -1540,8 +1455,9 @@ return /******/ (function(modules) { // webpackBootstrap
1540
1455
  var commentNode = xml.createComment('\n' + 'Source URL: ' + holderURL + generatorComment);
1541
1456
  var holderCSS = xml.createCDATASection(textCSSRule);
1542
1457
  var styleEl = svg.querySelector('style');
1458
+ var bg = root.children.holderBg;
1543
1459
 
1544
- setAttr(sceneGroupEl, {
1460
+ DOM.setAttr(sceneGroupEl, {
1545
1461
  id: holderId
1546
1462
  });
1547
1463
 
@@ -1549,13 +1465,38 @@ return /******/ (function(modules) { // webpackBootstrap
1549
1465
  styleEl.appendChild(holderCSS);
1550
1466
 
1551
1467
  sceneGroupEl.appendChild(bgEl);
1468
+
1469
+ //todo: abstract this into a cross-browser SVG outline method
1470
+ if (bg.properties.outline) {
1471
+ var outlineEl = DOM.newEl('path', SVG_NS);
1472
+ var outlineWidth = bg.properties.outline.width;
1473
+ var outlineOffsetWidth = outlineWidth / 2;
1474
+ DOM.setAttr(outlineEl, {
1475
+ 'd': [
1476
+ 'M', outlineOffsetWidth, outlineOffsetWidth,
1477
+ 'H', bg.width - outlineOffsetWidth,
1478
+ 'V', bg.height - outlineOffsetWidth,
1479
+ 'H', outlineOffsetWidth,
1480
+ 'V', 0,
1481
+ 'M', 0, outlineOffsetWidth,
1482
+ 'L', bg.width, bg.height - outlineOffsetWidth,
1483
+ 'M', 0, bg.height - outlineOffsetWidth,
1484
+ 'L', bg.width, outlineOffsetWidth
1485
+ ].join(' '),
1486
+ 'stroke-width': bg.properties.outline.width,
1487
+ 'stroke': bg.properties.outline.fill,
1488
+ 'fill': 'none'
1489
+ });
1490
+ sceneGroupEl.appendChild(outlineEl);
1491
+ }
1492
+
1552
1493
  sceneGroupEl.appendChild(textGroupEl);
1553
1494
  svg.appendChild(sceneGroupEl);
1554
1495
 
1555
- setAttr(bgEl, {
1556
- 'width': root.children.holderBg.width,
1557
- 'height': root.children.holderBg.height,
1558
- 'fill': root.children.holderBg.properties.fill
1496
+ DOM.setAttr(bgEl, {
1497
+ 'width': bg.width,
1498
+ 'height': bg.height,
1499
+ 'fill': bg.properties.fill
1559
1500
  });
1560
1501
 
1561
1502
  textGroup.y += tpdata.boundingBox.height * 0.8;
@@ -1567,10 +1508,10 @@ return /******/ (function(modules) { // webpackBootstrap
1567
1508
  var x = textGroup.x + line.x + word.x;
1568
1509
  var y = textGroup.y + line.y + word.y;
1569
1510
 
1570
- var textEl = newEl('text', SVG_NS);
1511
+ var textEl = DOM.newEl('text', SVG_NS);
1571
1512
  var textNode = document.createTextNode(null);
1572
1513
 
1573
- setAttr(textEl, {
1514
+ DOM.setAttr(textEl, {
1574
1515
  'x': x,
1575
1516
  'y': y
1576
1517
  });
@@ -1582,152 +1523,13 @@ return /******/ (function(modules) { // webpackBootstrap
1582
1523
  }
1583
1524
 
1584
1525
  //todo: factor the background check up the chain, perhaps only return reference
1585
- var svgString = svgStringToDataURI(serializeSVG(svg, renderSettings.engineSettings), renderSettings.mode === 'background');
1526
+ var svgString = SVG.svgStringToDataURI(SVG.serializeSVG(svg, renderSettings.engineSettings), renderSettings.mode === 'background');
1586
1527
  return svgString;
1587
1528
  };
1588
1529
  })();
1589
1530
 
1590
1531
  //Helpers
1591
1532
 
1592
- //todo: move svg-related helpers to a dedicated file
1593
-
1594
- /**
1595
- * Converts serialized SVG to a string suitable for data URI use
1596
- * @param svgString Serialized SVG string
1597
- * @param [base64] Use base64 encoding for data URI
1598
- */
1599
- var svgStringToDataURI = function() {
1600
- var rawPrefix = 'data:image/svg+xml;charset=UTF-8,';
1601
- var base64Prefix = 'data:image/svg+xml;charset=UTF-8;base64,';
1602
-
1603
- return function(svgString, base64) {
1604
- if (base64) {
1605
- return base64Prefix + btoa(unescape(encodeURIComponent(svgString)));
1606
- } else {
1607
- return rawPrefix + encodeURIComponent(svgString);
1608
- }
1609
- };
1610
- }();
1611
-
1612
- /**
1613
- * Generic new DOM element function
1614
- *
1615
- * @private
1616
- * @param tag Tag to create
1617
- * @param namespace Optional namespace value
1618
- */
1619
- function newEl(tag, namespace) {
1620
- if (namespace == null) {
1621
- return document.createElement(tag);
1622
- } else {
1623
- return document.createElementNS(namespace, tag);
1624
- }
1625
- }
1626
-
1627
- /**
1628
- * Generic setAttribute function
1629
- *
1630
- * @private
1631
- * @param el Reference to DOM element
1632
- * @param attrs Object with attribute keys and values
1633
- */
1634
- function setAttr(el, attrs) {
1635
- for (var a in attrs) {
1636
- el.setAttribute(a, attrs[a]);
1637
- }
1638
- }
1639
-
1640
- /**
1641
- * Generic SVG element creation function
1642
- *
1643
- * @private
1644
- * @param svg SVG context, set to null if new
1645
- * @param width Document width
1646
- * @param height Document height
1647
- */
1648
- function initSVG(svg, width, height) {
1649
- var defs, style;
1650
-
1651
- if (svg == null) {
1652
- svg = newEl('svg', SVG_NS);
1653
- defs = newEl('defs', SVG_NS);
1654
- style = newEl('style', SVG_NS);
1655
- setAttr(style, {
1656
- 'type': 'text/css'
1657
- });
1658
- defs.appendChild(style);
1659
- svg.appendChild(defs);
1660
- } else {
1661
- style = svg.querySelector('style');
1662
- }
1663
-
1664
- //IE throws an exception if this is set and Chrome requires it to be set
1665
- if (svg.webkitMatchesSelector) {
1666
- svg.setAttribute('xmlns', SVG_NS);
1667
- }
1668
-
1669
- //Remove comment nodes
1670
- for (var i = 0; i < svg.childNodes.length; i++) {
1671
- if (svg.childNodes[i].nodeType === NODE_TYPE_COMMENT) {
1672
- svg.removeChild(svg.childNodes[i]);
1673
- }
1674
- }
1675
-
1676
- //Remove CSS
1677
- while (style.childNodes.length) {
1678
- style.removeChild(style.childNodes[0]);
1679
- }
1680
-
1681
- setAttr(svg, {
1682
- 'width': width,
1683
- 'height': height,
1684
- 'viewBox': '0 0 ' + width + ' ' + height,
1685
- 'preserveAspectRatio': 'none'
1686
- });
1687
-
1688
- return svg;
1689
- }
1690
-
1691
- /**
1692
- * Returns serialized SVG with XML processing instructions
1693
- *
1694
- * @private
1695
- * @param svg SVG context
1696
- * @param stylesheets CSS stylesheets to include
1697
- */
1698
- function serializeSVG(svg, engineSettings) {
1699
- if (!global.XMLSerializer) return;
1700
- var serializer = new XMLSerializer();
1701
- var svgCSS = '';
1702
- var stylesheets = engineSettings.stylesheets;
1703
-
1704
- //External stylesheets: Processing Instruction method
1705
- if (engineSettings.svgXMLStylesheet) {
1706
- var xml = createXML();
1707
- //Add <?xml-stylesheet ?> directives
1708
- for (var i = stylesheets.length - 1; i >= 0; i--) {
1709
- var csspi = xml.createProcessingInstruction('xml-stylesheet', 'href="' + stylesheets[i] + '" rel="stylesheet"');
1710
- xml.insertBefore(csspi, xml.firstChild);
1711
- }
1712
-
1713
- xml.removeChild(xml.documentElement);
1714
- svgCSS = serializer.serializeToString(xml);
1715
- }
1716
-
1717
- var svgText = serializer.serializeToString(svg);
1718
- svgText = svgText.replace(/\&amp;(\#[0-9]{2,}\;)/g, '&$1');
1719
- return svgCSS + svgText;
1720
- }
1721
-
1722
- /**
1723
- * Creates a XML document
1724
- * @private
1725
- */
1726
- function createXML() {
1727
- if (!global.DOMParser) return;
1728
- return new DOMParser().parseFromString('<xml />', 'application/xml');
1729
- }
1730
-
1731
1533
  /**
1732
1534
  * Prevents a function from being called too often, waits until a timer elapses to call it again
1733
1535
  *
@@ -1769,6 +1571,7 @@ return /******/ (function(modules) { // webpackBootstrap
1769
1571
  supportsCanvas: false,
1770
1572
  supportsSVG: false,
1771
1573
  lineWrapRatio: 0.9,
1574
+ dataAttr: 'data-src',
1772
1575
  renderers: ['html', 'canvas', 'svg']
1773
1576
  };
1774
1577
 
@@ -1785,8 +1588,7 @@ return /******/ (function(modules) { // webpackBootstrap
1785
1588
  invisibleId: 0,
1786
1589
  visibilityCheckStarted: false,
1787
1590
  debounceTimer: null,
1788
- cache: {},
1789
- dataAttr: 'data-src'
1591
+ cache: {}
1790
1592
  };
1791
1593
 
1792
1594
  //Pre-flight
@@ -1795,7 +1597,7 @@ return /******/ (function(modules) { // webpackBootstrap
1795
1597
  var devicePixelRatio = 1,
1796
1598
  backingStoreRatio = 1;
1797
1599
 
1798
- var canvas = newEl('canvas');
1600
+ var canvas = DOM.newEl('canvas');
1799
1601
  var ctx = null;
1800
1602
 
1801
1603
  if (canvas.getContext) {
@@ -1847,34 +1649,142 @@ return /******/ (function(modules) { // webpackBootstrap
1847
1649
  /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
1848
1650
 
1849
1651
  /***/ },
1850
- /* 1 */
1652
+ /* 2 */
1851
1653
  /***/ function(module, exports, __webpack_require__) {
1852
1654
 
1853
- /*!
1854
- * onDomReady.js 1.4.0 (c) 2013 Tubal Martin - MIT license
1655
+ //Modified version of component/querystring
1656
+ //Changes: updated dependencies, dot notation parsing, JSHint fixes
1657
+ //Fork at https://github.com/imsky/querystring
1658
+
1659
+ /**
1660
+ * Module dependencies.
1661
+ */
1662
+
1663
+ var encode = encodeURIComponent;
1664
+ var decode = decodeURIComponent;
1665
+ var trim = __webpack_require__(10);
1666
+ var type = __webpack_require__(9);
1667
+
1668
+ var arrayRegex = /(\w+)\[(\d+)\]/;
1669
+ var objectRegex = /\w+\.\w+/;
1670
+
1671
+ /**
1672
+ * Parse the given query `str`.
1855
1673
  *
1856
- * Specially modified to work with Holder.js
1674
+ * @param {String} str
1675
+ * @return {Object}
1676
+ * @api public
1857
1677
  */
1858
1678
 
1859
- function _onDomReady(win) {
1860
- //Lazy loading fix for Firefox < 3.6
1861
- //http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
1862
- if (document.readyState == null && document.addEventListener) {
1863
- document.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
1864
- document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
1865
- document.readyState = "complete";
1866
- }, false);
1867
- document.readyState = "loading";
1679
+ exports.parse = function(str){
1680
+ if ('string' !== typeof str) return {};
1681
+
1682
+ str = trim(str);
1683
+ if ('' === str) return {};
1684
+ if ('?' === str.charAt(0)) str = str.slice(1);
1685
+
1686
+ var obj = {};
1687
+ var pairs = str.split('&');
1688
+ for (var i = 0; i < pairs.length; i++) {
1689
+ var parts = pairs[i].split('=');
1690
+ var key = decode(parts[0]);
1691
+ var m, ctx, prop;
1692
+
1693
+ if (m = arrayRegex.exec(key)) {
1694
+ obj[m[1]] = obj[m[1]] || [];
1695
+ obj[m[1]][m[2]] = decode(parts[1]);
1696
+ continue;
1868
1697
  }
1869
-
1870
- var doc = win.document,
1871
- docElem = doc.documentElement,
1872
-
1873
- LOAD = "load",
1874
- FALSE = false,
1875
- ONLOAD = "on"+LOAD,
1876
- COMPLETE = "complete",
1877
- READYSTATE = "readyState",
1698
+
1699
+ if (m = objectRegex.test(key)) {
1700
+ m = key.split('.');
1701
+ ctx = obj;
1702
+
1703
+ while (m.length) {
1704
+ prop = m.shift();
1705
+
1706
+ if (!prop.length) continue;
1707
+
1708
+ if (!ctx[prop]) {
1709
+ ctx[prop] = {};
1710
+ } else if (ctx[prop] && typeof ctx[prop] !== 'object') {
1711
+ break;
1712
+ }
1713
+
1714
+ if (!m.length) {
1715
+ ctx[prop] = decode(parts[1]);
1716
+ }
1717
+
1718
+ ctx = ctx[prop];
1719
+ }
1720
+
1721
+ continue;
1722
+ }
1723
+
1724
+ obj[parts[0]] = null == parts[1] ? '' : decode(parts[1]);
1725
+ }
1726
+
1727
+ return obj;
1728
+ };
1729
+
1730
+ /**
1731
+ * Stringify the given `obj`.
1732
+ *
1733
+ * @param {Object} obj
1734
+ * @return {String}
1735
+ * @api public
1736
+ */
1737
+
1738
+ exports.stringify = function(obj){
1739
+ if (!obj) return '';
1740
+ var pairs = [];
1741
+
1742
+ for (var key in obj) {
1743
+ var value = obj[key];
1744
+
1745
+ if ('array' == type(value)) {
1746
+ for (var i = 0; i < value.length; ++i) {
1747
+ pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i]));
1748
+ }
1749
+ continue;
1750
+ }
1751
+
1752
+ pairs.push(encode(key) + '=' + encode(obj[key]));
1753
+ }
1754
+
1755
+ return pairs.join('&');
1756
+ };
1757
+
1758
+
1759
+ /***/ },
1760
+ /* 3 */
1761
+ /***/ function(module, exports, __webpack_require__) {
1762
+
1763
+ /*!
1764
+ * onDomReady.js 1.4.0 (c) 2013 Tubal Martin - MIT license
1765
+ *
1766
+ * Specially modified to work with Holder.js
1767
+ */
1768
+
1769
+ function _onDomReady(win) {
1770
+ //Lazy loading fix for Firefox < 3.6
1771
+ //http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
1772
+ if (document.readyState == null && document.addEventListener) {
1773
+ document.addEventListener("DOMContentLoaded", function DOMContentLoaded() {
1774
+ document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
1775
+ document.readyState = "complete";
1776
+ }, false);
1777
+ document.readyState = "loading";
1778
+ }
1779
+
1780
+ var doc = win.document,
1781
+ docElem = doc.documentElement,
1782
+
1783
+ LOAD = "load",
1784
+ FALSE = false,
1785
+ ONLOAD = "on"+LOAD,
1786
+ COMPLETE = "complete",
1787
+ READYSTATE = "readyState",
1878
1788
  ATTACHEVENT = "attachEvent",
1879
1789
  DETACHEVENT = "detachEvent",
1880
1790
  ADDEVENTLISTENER = "addEventListener",
@@ -2007,11 +1917,9 @@ return /******/ (function(modules) { // webpackBootstrap
2007
1917
  module.exports = typeof window !== "undefined" && _onDomReady(window);
2008
1918
 
2009
1919
  /***/ },
2010
- /* 2 */
1920
+ /* 4 */
2011
1921
  /***/ function(module, exports, __webpack_require__) {
2012
1922
 
2013
- var augment = __webpack_require__(5);
2014
-
2015
1923
  var SceneGraph = function(sceneProperties) {
2016
1924
  var nodeCount = 1;
2017
1925
 
@@ -2023,90 +1931,96 @@ return /******/ (function(modules) { // webpackBootstrap
2023
1931
  return parent;
2024
1932
  }
2025
1933
 
2026
- var SceneNode = augment.defclass({
2027
- constructor: function(name) {
2028
- nodeCount++;
2029
- this.parent = null;
2030
- this.children = {};
2031
- this.id = nodeCount;
2032
- this.name = 'n' + nodeCount;
2033
- if (name != null) {
2034
- this.name = name;
2035
- }
2036
- this.x = 0;
2037
- this.y = 0;
2038
- this.z = 0;
2039
- this.width = 0;
2040
- this.height = 0;
2041
- },
2042
- resize: function(width, height) {
2043
- if (width != null) {
2044
- this.width = width;
2045
- }
2046
- if (height != null) {
2047
- this.height = height;
2048
- }
2049
- },
2050
- moveTo: function(x, y, z) {
2051
- this.x = x != null ? x : this.x;
2052
- this.y = y != null ? y : this.y;
2053
- this.z = z != null ? z : this.z;
2054
- },
2055
- add: function(child) {
2056
- var name = child.name;
2057
- if (this.children[name] == null) {
2058
- this.children[name] = child;
2059
- child.parent = this;
2060
- } else {
2061
- throw 'SceneGraph: child with that name already exists: ' + name;
2062
- }
1934
+ var SceneNode = function(name) {
1935
+ nodeCount++;
1936
+ this.parent = null;
1937
+ this.children = {};
1938
+ this.id = nodeCount;
1939
+ this.name = 'n' + nodeCount;
1940
+ if (typeof name !== 'undefined') {
1941
+ this.name = name;
1942
+ }
1943
+ this.x = this.y = this.z = 0;
1944
+ this.width = this.height = 0;
1945
+ };
1946
+
1947
+ SceneNode.prototype.resize = function(width, height) {
1948
+ if (width != null) {
1949
+ this.width = width;
2063
1950
  }
2064
- });
1951
+ if (height != null) {
1952
+ this.height = height;
1953
+ }
1954
+ };
2065
1955
 
2066
- var RootNode = augment(SceneNode, function(uber) {
2067
- this.constructor = function() {
2068
- uber.constructor.call(this, 'root');
2069
- this.properties = sceneProperties;
2070
- };
2071
- });
1956
+ SceneNode.prototype.moveTo = function(x, y, z) {
1957
+ this.x = x != null ? x : this.x;
1958
+ this.y = y != null ? y : this.y;
1959
+ this.z = z != null ? z : this.z;
1960
+ };
2072
1961
 
2073
- var Shape = augment(SceneNode, function(uber) {
2074
- function constructor(name, props) {
2075
- uber.constructor.call(this, name);
2076
- this.properties = {
2077
- fill: '#000'
2078
- };
2079
- if (props != null) {
2080
- merge(this.properties, props);
2081
- } else if (name != null && typeof name !== 'string') {
2082
- throw 'SceneGraph: invalid node name';
2083
- }
1962
+ SceneNode.prototype.add = function(child) {
1963
+ var name = child.name;
1964
+ if (typeof this.children[name] === 'undefined') {
1965
+ this.children[name] = child;
1966
+ child.parent = this;
1967
+ } else {
1968
+ throw 'SceneGraph: child already exists: ' + name;
2084
1969
  }
1970
+ };
2085
1971
 
2086
- this.Group = augment.extend(this, {
2087
- constructor: constructor,
2088
- type: 'group'
2089
- });
1972
+ var RootNode = function() {
1973
+ SceneNode.call(this, 'root');
1974
+ this.properties = sceneProperties;
1975
+ };
2090
1976
 
2091
- this.Rect = augment.extend(this, {
2092
- constructor: constructor,
2093
- type: 'rect'
2094
- });
1977
+ RootNode.prototype = new SceneNode();
2095
1978
 
2096
- this.Text = augment.extend(this, {
2097
- constructor: function(text) {
2098
- constructor.call(this);
2099
- this.properties.text = text;
2100
- },
2101
- type: 'text'
2102
- });
2103
- });
1979
+ var Shape = function(name, props) {
1980
+ SceneNode.call(this, name);
1981
+ this.properties = {
1982
+ 'fill': '#000000'
1983
+ };
1984
+ if (typeof props !== 'undefined') {
1985
+ merge(this.properties, props);
1986
+ } else if (typeof name !== 'undefined' && typeof name !== 'string') {
1987
+ throw 'SceneGraph: invalid node name';
1988
+ }
1989
+ };
1990
+
1991
+ Shape.prototype = new SceneNode();
1992
+
1993
+ var Group = function() {
1994
+ Shape.apply(this, arguments);
1995
+ this.type = 'group';
1996
+ };
1997
+
1998
+ Group.prototype = new Shape();
1999
+
2000
+ var Rect = function() {
2001
+ Shape.apply(this, arguments);
2002
+ this.type = 'rect';
2003
+ };
2004
+
2005
+ Rect.prototype = new Shape();
2006
+
2007
+ var Text = function(text) {
2008
+ Shape.call(this);
2009
+ this.type = 'text';
2010
+ this.properties.text = text;
2011
+ };
2012
+
2013
+ Text.prototype = new Shape();
2104
2014
 
2105
2015
  var root = new RootNode();
2106
2016
 
2107
- this.Shape = Shape;
2108
- this.root = root;
2017
+ this.Shape = {
2018
+ 'Rect': Rect,
2019
+ 'Text': Text,
2020
+ 'Group': Group
2021
+ };
2109
2022
 
2023
+ this.root = root;
2110
2024
  return this;
2111
2025
  };
2112
2026
 
@@ -2114,10 +2028,10 @@ return /******/ (function(modules) { // webpackBootstrap
2114
2028
 
2115
2029
 
2116
2030
  /***/ },
2117
- /* 3 */
2031
+ /* 5 */
2118
2032
  /***/ function(module, exports, __webpack_require__) {
2119
2033
 
2120
- /* WEBPACK VAR INJECTION */(function(global) {/**
2034
+ /**
2121
2035
  * Shallow object clone and merge
2122
2036
  *
2123
2037
  * @param a Object A
@@ -2175,30 +2089,6 @@ return /******/ (function(modules) { // webpackBootstrap
2175
2089
  return buf.join('');
2176
2090
  };
2177
2091
 
2178
-
2179
- /**
2180
- * Converts a value into an array of DOM nodes
2181
- *
2182
- * @param val A string, a NodeList, a Node, or an HTMLCollection
2183
- */
2184
- exports.getNodeArray = function(val) {
2185
- var retval = null;
2186
- if (typeof(val) == 'string') {
2187
- retval = document.querySelectorAll(val);
2188
- } else if (global.NodeList && val instanceof global.NodeList) {
2189
- retval = val;
2190
- } else if (global.Node && val instanceof global.Node) {
2191
- retval = [val];
2192
- } else if (global.HTMLCollection && val instanceof global.HTMLCollection) {
2193
- retval = val;
2194
- } else if (val instanceof Array) {
2195
- retval = val;
2196
- } else if (val === null) {
2197
- retval = [];
2198
- }
2199
- return retval;
2200
- };
2201
-
2202
2092
  /**
2203
2093
  * Checks if an image exists
2204
2094
  *
@@ -2258,170 +2148,339 @@ return /******/ (function(modules) { // webpackBootstrap
2258
2148
  return !!val;
2259
2149
  };
2260
2150
 
2261
- /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
2262
2151
 
2263
2152
  /***/ },
2264
- /* 4 */
2153
+ /* 6 */
2265
2154
  /***/ function(module, exports, __webpack_require__) {
2266
2155
 
2267
- //Modified version of component/querystring
2268
- //Changes: updated dependencies, dot notation parsing, JSHint fixes
2269
- //Fork at https://github.com/imsky/querystring
2270
-
2271
- /**
2272
- * Module dependencies.
2273
- */
2274
-
2275
- var encode = encodeURIComponent;
2276
- var decode = decodeURIComponent;
2277
- var trim = __webpack_require__(6);
2278
- var type = __webpack_require__(7);
2156
+ /* WEBPACK VAR INJECTION */(function(global) {var DOM = __webpack_require__(7);
2279
2157
 
2280
- var arrayRegex = /(\w+)\[(\d+)\]/;
2281
- var objectRegex = /\w+\.\w+/;
2158
+ var SVG_NS = 'http://www.w3.org/2000/svg';
2159
+ var NODE_TYPE_COMMENT = 8;
2282
2160
 
2283
2161
  /**
2284
- * Parse the given query `str`.
2162
+ * Generic SVG element creation function
2285
2163
  *
2286
- * @param {String} str
2287
- * @return {Object}
2288
- * @api public
2164
+ * @private
2165
+ * @param svg SVG context, set to null if new
2166
+ * @param width Document width
2167
+ * @param height Document height
2289
2168
  */
2169
+ exports.initSVG = function(svg, width, height) {
2170
+ var defs, style, initialize = false;
2290
2171
 
2291
- exports.parse = function(str){
2292
- if ('string' !== typeof str) return {};
2172
+ if (svg && svg.querySelector) {
2173
+ style = svg.querySelector('style');
2174
+ if (style === null) {
2175
+ initialize = true;
2176
+ }
2177
+ } else {
2178
+ svg = DOM.newEl('svg', SVG_NS);
2179
+ initialize = true;
2180
+ }
2293
2181
 
2294
- str = trim(str);
2295
- if ('' === str) return {};
2296
- if ('?' === str.charAt(0)) str = str.slice(1);
2182
+ if (initialize) {
2183
+ defs = DOM.newEl('defs', SVG_NS);
2184
+ style = DOM.newEl('style', SVG_NS);
2185
+ DOM.setAttr(style, {
2186
+ 'type': 'text/css'
2187
+ });
2188
+ defs.appendChild(style);
2189
+ svg.appendChild(defs);
2190
+ }
2297
2191
 
2298
- var obj = {};
2299
- var pairs = str.split('&');
2300
- for (var i = 0; i < pairs.length; i++) {
2301
- var parts = pairs[i].split('=');
2302
- var key = decode(parts[0]);
2303
- var m, ctx, prop;
2192
+ //IE throws an exception if this is set and Chrome requires it to be set
2193
+ if (svg.webkitMatchesSelector) {
2194
+ svg.setAttribute('xmlns', SVG_NS);
2195
+ }
2304
2196
 
2305
- if (m = arrayRegex.exec(key)) {
2306
- obj[m[1]] = obj[m[1]] || [];
2307
- obj[m[1]][m[2]] = decode(parts[1]);
2308
- continue;
2197
+ //Remove comment nodes
2198
+ for (var i = 0; i < svg.childNodes.length; i++) {
2199
+ if (svg.childNodes[i].nodeType === NODE_TYPE_COMMENT) {
2200
+ svg.removeChild(svg.childNodes[i]);
2201
+ }
2309
2202
  }
2310
2203
 
2311
- if (m = objectRegex.test(key)) {
2312
- m = key.split('.');
2313
- ctx = obj;
2314
-
2315
- while (m.length) {
2316
- prop = m.shift();
2204
+ //Remove CSS
2205
+ while (style.childNodes.length) {
2206
+ style.removeChild(style.childNodes[0]);
2207
+ }
2317
2208
 
2318
- if (!prop.length) continue;
2209
+ DOM.setAttr(svg, {
2210
+ 'width': width,
2211
+ 'height': height,
2212
+ 'viewBox': '0 0 ' + width + ' ' + height,
2213
+ 'preserveAspectRatio': 'none'
2214
+ });
2319
2215
 
2320
- if (!ctx[prop]) {
2321
- ctx[prop] = {};
2322
- } else if (ctx[prop] && typeof ctx[prop] !== 'object') {
2323
- break;
2216
+ return svg;
2217
+ };
2218
+
2219
+ /**
2220
+ * Converts serialized SVG to a string suitable for data URI use
2221
+ * @param svgString Serialized SVG string
2222
+ * @param [base64] Use base64 encoding for data URI
2223
+ */
2224
+ exports.svgStringToDataURI = function() {
2225
+ var rawPrefix = 'data:image/svg+xml;charset=UTF-8,';
2226
+ var base64Prefix = 'data:image/svg+xml;charset=UTF-8;base64,';
2227
+
2228
+ return function(svgString, base64) {
2229
+ if (base64) {
2230
+ return base64Prefix + btoa(unescape(encodeURIComponent(svgString)));
2231
+ } else {
2232
+ return rawPrefix + encodeURIComponent(svgString);
2324
2233
  }
2234
+ };
2235
+ }();
2325
2236
 
2326
- if (!m.length) {
2327
- ctx[prop] = decode(parts[1]);
2237
+ /**
2238
+ * Returns serialized SVG with XML processing instructions
2239
+ *
2240
+ * @private
2241
+ * @param svg SVG context
2242
+ * @param stylesheets CSS stylesheets to include
2243
+ */
2244
+ exports.serializeSVG = function(svg, engineSettings) {
2245
+ if (!global.XMLSerializer) return;
2246
+ var serializer = new XMLSerializer();
2247
+ var svgCSS = '';
2248
+ var stylesheets = engineSettings.stylesheets;
2249
+
2250
+ //External stylesheets: Processing Instruction method
2251
+ if (engineSettings.svgXMLStylesheet) {
2252
+ var xml = DOM.createXML();
2253
+ //Add <?xml-stylesheet ?> directives
2254
+ for (var i = stylesheets.length - 1; i >= 0; i--) {
2255
+ var csspi = xml.createProcessingInstruction('xml-stylesheet', 'href="' + stylesheets[i] + '" rel="stylesheet"');
2256
+ xml.insertBefore(csspi, xml.firstChild);
2328
2257
  }
2329
2258
 
2330
- ctx = ctx[prop];
2331
- }
2259
+ xml.removeChild(xml.documentElement);
2260
+ svgCSS = serializer.serializeToString(xml);
2261
+ }
2332
2262
 
2333
- continue;
2263
+ var svgText = serializer.serializeToString(svg);
2264
+ svgText = svgText.replace(/\&amp;(\#[0-9]{2,}\;)/g, '&$1');
2265
+ return svgCSS + svgText;
2266
+ };
2267
+
2268
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
2269
+
2270
+ /***/ },
2271
+ /* 7 */
2272
+ /***/ function(module, exports, __webpack_require__) {
2273
+
2274
+ /* WEBPACK VAR INJECTION */(function(global) {/**
2275
+ * Generic new DOM element function
2276
+ *
2277
+ * @private
2278
+ * @param tag Tag to create
2279
+ * @param namespace Optional namespace value
2280
+ */
2281
+ exports.newEl = function(tag, namespace) {
2282
+ if (!global.document) return;
2283
+
2284
+ if (namespace == null) {
2285
+ return document.createElement(tag);
2286
+ } else {
2287
+ return document.createElementNS(namespace, tag);
2334
2288
  }
2289
+ };
2335
2290
 
2336
- obj[parts[0]] = null == parts[1] ? '' : decode(parts[1]);
2337
- }
2291
+ /**
2292
+ * Generic setAttribute function
2293
+ *
2294
+ * @private
2295
+ * @param el Reference to DOM element
2296
+ * @param attrs Object with attribute keys and values
2297
+ */
2298
+ exports.setAttr = function(el, attrs) {
2299
+ for (var a in attrs) {
2300
+ el.setAttribute(a, attrs[a]);
2301
+ }
2302
+ };
2338
2303
 
2339
- return obj;
2304
+ /**
2305
+ * Creates a XML document
2306
+ * @private
2307
+ */
2308
+ exports.createXML = function() {
2309
+ if (!global.DOMParser) return;
2310
+ return new DOMParser().parseFromString('<xml />', 'application/xml');
2340
2311
  };
2341
2312
 
2342
2313
  /**
2343
- * Stringify the given `obj`.
2314
+ * Converts a value into an array of DOM nodes
2344
2315
  *
2345
- * @param {Object} obj
2346
- * @return {String}
2347
- * @api public
2316
+ * @param val A string, a NodeList, a Node, or an HTMLCollection
2348
2317
  */
2318
+ exports.getNodeArray = function(val) {
2319
+ var retval = null;
2320
+ if (typeof(val) == 'string') {
2321
+ retval = document.querySelectorAll(val);
2322
+ } else if (global.NodeList && val instanceof global.NodeList) {
2323
+ retval = val;
2324
+ } else if (global.Node && val instanceof global.Node) {
2325
+ retval = [val];
2326
+ } else if (global.HTMLCollection && val instanceof global.HTMLCollection) {
2327
+ retval = val;
2328
+ } else if (val instanceof Array) {
2329
+ retval = val;
2330
+ } else if (val === null) {
2331
+ retval = [];
2332
+ }
2333
+ return retval;
2334
+ };
2349
2335
 
2350
- exports.stringify = function(obj){
2351
- if (!obj) return '';
2352
- var pairs = [];
2336
+ /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
2353
2337
 
2354
- for (var key in obj) {
2355
- var value = obj[key];
2338
+ /***/ },
2339
+ /* 8 */
2340
+ /***/ function(module, exports, __webpack_require__) {
2356
2341
 
2357
- if ('array' == type(value)) {
2358
- for (var i = 0; i < value.length; ++i) {
2359
- pairs.push(encode(key + '[' + i + ']') + '=' + encode(value[i]));
2360
- }
2361
- continue;
2342
+ var Color = function (color, options) {
2343
+ //todo: support array->color conversion
2344
+ //todo: support rgba, hsla, and rrggbbaa notation
2345
+ if (typeof color !== 'string') return;
2346
+
2347
+ if (color.charAt(0) === '#') {
2348
+ color = color.slice(1);
2362
2349
  }
2363
2350
 
2364
- pairs.push(encode(key) + '=' + encode(obj[key]));
2365
- }
2351
+ if (/[^a-f0-9]+/i.test(color)) return;
2366
2352
 
2367
- return pairs.join('&');
2353
+ if (color.length === 3) {
2354
+ color = color.replace(/./g, '$&$&');
2355
+ }
2356
+
2357
+ if (color.length !== 6) return;
2358
+
2359
+ this.alpha = 1;
2360
+
2361
+ if (options) {
2362
+ this.alpha = options.alpha || this.alpha;
2363
+ }
2364
+
2365
+ colorSet.call(this, parseInt(color, 16));
2368
2366
  };
2369
2367
 
2368
+ Color.rgbToHex = function (r, g, b) {
2369
+ return (((r | 0) << 16) + ((g | 0) << 8) + (b | 0)).toString(16);
2370
+ };
2370
2371
 
2371
- /***/ },
2372
- /* 5 */
2373
- /***/ function(module, exports, __webpack_require__) {
2372
+ /**
2373
+ * Sets the color from a raw RGB888 integer
2374
+ * @param raw RGB888 representation of color
2375
+ */
2376
+ //todo: refactor into a more generic method
2377
+ function colorSet (raw) {
2378
+ this.rgb = {};
2379
+ this.yuv = {};
2380
+ this.raw = raw;
2381
+
2382
+ this.rgb.r = (raw & 0xFF0000) >> 16;
2383
+ this.rgb.g = (raw & 0x00FF00) >> 8;
2384
+ this.rgb.b = (raw & 0x0000FF);
2385
+
2386
+ // BT.709
2387
+ this.yuv.y = 0.2126 * this.rgb.r + 0.7152 * this.rgb.g + 0.0722 * this.rgb.b;
2388
+ this.yuv.u = -0.09991 * this.rgb.r - 0.33609 * this.rgb.g + 0.436 * this.rgb.b;
2389
+ this.yuv.v = 0.615 * this.rgb.r - 0.55861 * this.rgb.g - 0.05639 * this.rgb.b;
2390
+
2391
+ return this;
2392
+ }
2374
2393
 
2375
- var Factory = function () {};
2376
- var slice = Array.prototype.slice;
2377
-
2378
- var augment = function (base, body) {
2379
- var uber = Factory.prototype = typeof base === "function" ? base.prototype : base;
2380
- var prototype = new Factory(), properties = body.apply(prototype, slice.call(arguments, 2).concat(uber));
2381
- if (typeof properties === "object") for (var key in properties) prototype[key] = properties[key];
2382
- if (!prototype.hasOwnProperty("constructor")) return prototype;
2383
- var constructor = prototype.constructor;
2384
- constructor.prototype = prototype;
2385
- return constructor;
2394
+ /**
2395
+ * Lighten or darken a color
2396
+ * @param multiplier Amount to lighten or darken (-1 to 1)
2397
+ */
2398
+ Color.prototype.lighten = function (multiplier) {
2399
+ var r = this.rgb.r;
2400
+ var g = this.rgb.g;
2401
+ var b = this.rgb.b;
2402
+
2403
+ var m = (255 * multiplier) | 0;
2404
+
2405
+ return new Color(Color.rgbToHex(r + m, g + m, b + m));
2386
2406
  };
2387
2407
 
2388
- augment.defclass = function (prototype) {
2389
- var constructor = prototype.constructor;
2390
- constructor.prototype = prototype;
2391
- return constructor;
2408
+ /**
2409
+ * Output color in hex format
2410
+ * @param addHash Add a hash character to the beginning of the output
2411
+ */
2412
+ Color.prototype.toHex = function (addHash) {
2413
+ return (addHash ? '#' : '') + this.raw.toString(16);
2392
2414
  };
2393
2415
 
2394
- augment.extend = function (base, body) {
2395
- return augment(base, function (uber) {
2396
- this.uber = uber;
2397
- return body;
2398
- });
2416
+ /**
2417
+ * Returns whether or not current color is lighter than another color
2418
+ * @param color Color to compare against
2419
+ */
2420
+ Color.prototype.lighterThan = function (color) {
2421
+ if (!(color instanceof Color)) {
2422
+ color = new Color(color);
2423
+ }
2424
+
2425
+ return this.yuv.y > color.yuv.y;
2399
2426
  };
2400
2427
 
2401
- module.exports = augment;
2428
+ /**
2429
+ * Returns the result of mixing current color with another color
2430
+ * @param color Color to mix with
2431
+ * @param multiplier How much to mix with the other color
2432
+ */
2433
+ /*
2434
+ Color.prototype.mix = function (color, multiplier) {
2435
+ if (!(color instanceof Color)) {
2436
+ color = new Color(color);
2437
+ }
2402
2438
 
2403
- /***/ },
2404
- /* 6 */
2405
- /***/ function(module, exports, __webpack_require__) {
2439
+ var r = this.rgb.r;
2440
+ var g = this.rgb.g;
2441
+ var b = this.rgb.b;
2442
+ var a = this.alpha;
2406
2443
 
2407
-
2408
- exports = module.exports = trim;
2444
+ var m = typeof multiplier !== 'undefined' ? multiplier : 0.5;
2409
2445
 
2410
- function trim(str){
2411
- return str.replace(/^\s*|\s*$/g, '');
2412
- }
2446
+ //todo: write a lerp function
2447
+ r = r + m * (color.rgb.r - r);
2448
+ g = g + m * (color.rgb.g - g);
2449
+ b = b + m * (color.rgb.b - b);
2450
+ a = a + m * (color.alpha - a);
2413
2451
 
2414
- exports.left = function(str){
2415
- return str.replace(/^\s*/, '');
2452
+ return new Color(Color.rgbToHex(r, g, b), {
2453
+ 'alpha': a
2454
+ });
2416
2455
  };
2456
+ */
2417
2457
 
2418
- exports.right = function(str){
2419
- return str.replace(/\s*$/, '');
2458
+ /**
2459
+ * Returns the result of blending another color on top of current color with alpha
2460
+ * @param color Color to blend on top of current color, i.e. "Ca"
2461
+ */
2462
+ //todo: see if .blendAlpha can be merged into .mix
2463
+ Color.prototype.blendAlpha = function (color) {
2464
+ if (!(color instanceof Color)) {
2465
+ color = new Color(color);
2466
+ }
2467
+
2468
+ var Ca = color;
2469
+ var Cb = this;
2470
+
2471
+ //todo: write alpha blending function
2472
+ var r = Ca.alpha * Ca.rgb.r + (1 - Ca.alpha) * Cb.rgb.r;
2473
+ var g = Ca.alpha * Ca.rgb.g + (1 - Ca.alpha) * Cb.rgb.g;
2474
+ var b = Ca.alpha * Ca.rgb.b + (1 - Ca.alpha) * Cb.rgb.b;
2475
+
2476
+ return new Color(Color.rgbToHex(r, g, b));
2420
2477
  };
2421
2478
 
2479
+ module.exports = Color;
2480
+
2422
2481
 
2423
2482
  /***/ },
2424
- /* 7 */
2483
+ /* 9 */
2425
2484
  /***/ function(module, exports, __webpack_require__) {
2426
2485
 
2427
2486
  /**
@@ -2460,6 +2519,26 @@ return /******/ (function(modules) { // webpackBootstrap
2460
2519
  };
2461
2520
 
2462
2521
 
2522
+ /***/ },
2523
+ /* 10 */
2524
+ /***/ function(module, exports, __webpack_require__) {
2525
+
2526
+
2527
+ exports = module.exports = trim;
2528
+
2529
+ function trim(str){
2530
+ return str.replace(/^\s*|\s*$/g, '');
2531
+ }
2532
+
2533
+ exports.left = function(str){
2534
+ return str.replace(/^\s*/, '');
2535
+ };
2536
+
2537
+ exports.right = function(str){
2538
+ return str.replace(/\s*$/, '');
2539
+ };
2540
+
2541
+
2463
2542
  /***/ }
2464
2543
  /******/ ])
2465
2544
  });