diff --git a/README.markdown b/README.markdown
index 251c16e..1a44f14 100644
--- a/README.markdown
+++ b/README.markdown
@@ -60,18 +60,10 @@ Then, when you open Inkscape, go to File > New, and choose InteractiveMockup fro
MagicMarkup is written in CoffeeScript and uses jQuery. You'll
need CoffeeScript installed to develop.
-You may install CoffeeScript either via:
+You may install CoffeeScript via:
- gem install coffee-script
+ npm install coffeescript
-...or...
-
- npm install coffee-script
-
-...Depending on if you are using Ruby & Gem or Node.js & NPM.
-As CoffeeScript is JavaScript, we suggest installing Node.js
-and using `npm` for installation.
-
Build magicmockup.js using `cake build` - this will compile magicmockup.coffee and concatenate it with the dependencies in the deps/ directory.
There is a really simple script included for helping you to
diff --git a/magicmockup.coffee b/magicmockup.coffee
index e0da03c..22597ca 100644
--- a/magicmockup.coffee
+++ b/magicmockup.coffee
@@ -36,7 +36,7 @@ $ = @jQuery
# Do the heavy lifting
# (right now, there's only "next" for switching pages; more to come)
_dispatch = (context, [command, val]) ->
- act =
+ act = {
load: (url) ->
url = url.shift()
window.location = url || val
@@ -57,7 +57,7 @@ $ = @jQuery
location = '' if location is defaultLayer
window.location.hash = location
-
+
show: (show_layers) ->
for layer in show_layers
$(layers[layer]).show()
@@ -80,9 +80,10 @@ $ = @jQuery
time = parseInt(time) * 1000
$(layers[layer])
.attr('opacity', 1)
- .animate svgOpacity: 0.0, time, easing, () ->
- # Reset opacity but hide
+ .animate { svgOpacity: 0.0 }, time, easing, () ->
+ # Reset opacity but hide
$(this).hide().attr 'opacity', 1
+ }
params = val?.split ','
act[command]?(params)
@@ -167,13 +168,13 @@ $ = @jQuery
# Alter hover CSS if there's a hover filter
if filter.hover
hover = if isHovered then "url(##{filter.hover})" else "none"
- $this.css filter: hover
+ $this.css({ filter: hover })
# Skip if already hoverable
return if $this.data('hoverable')
# We're handling the hoverable state now
- $this.data('hoverable', true).css(cursor: 'pointer')
+ $this.data('hoverable', true).css({ cursor: 'pointer' })
return
@@ -187,13 +188,14 @@ $ = @jQuery
$(window).bind 'hashchange', _showLayer
- $doc.delegate 'g'
- click : _handleClick
- hover : _handleHover
+ $doc.delegate 'g', {
+ click: _handleClick
+ hover: _handleHover
+ }
- {init} # Public exports
+ { init } # Public exports
# Hack to attach the init to for an unobtrusive SVG onload
-$('svg').attr onload: 'magicmockup.init()'
+$('svg').attr { onload: 'magicmockup.init()' }
diff --git a/magicmockup.js b/magicmockup.js
index 44d41d8..0f5edaf 100644
--- a/magicmockup.js
+++ b/magicmockup.js
@@ -9282,34 +9282,39 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
Please attribute the author if you use it. */
-(function($){$.fn.addClass=function(e){return function(d){d=d||'';return this.each(function(){if($.svg.isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));if($.inArray(a,b.split(/\s+/))==-1){b+=(b?' ':'')+a;(c.className?c.className.baseVal=b:c.setAttribute('class',b))}})}else{e.apply($(this),[d])}})}}($.fn.addClass);$.fn.removeClass=function(e){return function(d){d=d||'';return this.each(function(){if($.svg.isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));b=$.grep(b.split(/\s+/),function(n,i){return n!=a}).join(' ');(c.className?c.className.baseVal=b:c.setAttribute('class',b))})}else{e.apply($(this),[d])}})}}($.fn.removeClass);$.fn.toggleClass=function(c){return function(a,b){return this.each(function(){if($.svg.isSVGElem(this)){if(typeof b!=='boolean'){b=!$(this).hasClass(a)}$(this)[(b?'add':'remove')+'Class'](a)}else{c.apply($(this),[a,b])}})}}($.fn.toggleClass);$.fn.hasClass=function(d){return function(b){b=b||'';var c=false;this.each(function(){if($.svg.isSVGElem(this)){var a=(this.className?this.className.baseVal:this.getAttribute('class')).split(/\s+/);c=($.inArray(b,a)>-1)}else{c=(d.apply($(this),[b]))}return!c});return c}}($.fn.hasClass);$.fn.attr=function(h){return function(b,c,d){if(typeof b==='string'&&c===undefined){var e=h.apply(this,[b,c,d]);if(e&&e.baseVal&&e.baseVal.numberOfItems!=null){c='';e=e.baseVal;if(b=='transform'){for(var i=0;i']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['>']);$.expr.relative['']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['']);$.expr.relative['~']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['~']);$.expr.find.ID=function(d){return function(a,b,c){return($.svg.isSVGElem(b)?[b.ownerDocument.getElementById(a[1])]:d(a,b,c))}}($.expr.find.ID);var j=document.createElement('div');j.appendChild(document.createComment(''));if(j.getElementsByTagName('*').length>0){$.expr.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==='*'){var d=[];for(var i=0;c[i]||c.item(i);i++){if((c[i]||c.item(i)).nodeType===1){d.push(c[i]||c.item(i))}}c=d}return c}}$.expr.preFilter.CLASS=function(a,b,c,d,f,g){a=' '+a[1].replace(/\\/g,'')+' ';if(g){return a}for(var i=0,elem={};elem!=null;i++){elem=b[i];if(!elem){try{elem=b.item(i)}catch(e){}}if(elem){var h=(!$.svg.isSVGElem(elem)?elem.className:(elem.className?elem.className.baseVal:'')||elem.getAttribute('class'));if(f^(h&&(' '+h+' ').indexOf(a)>-1)){if(!c)d.push(elem)}else if(c){b[i]=false}}}return false};$.expr.filter.CLASS=function(a,b){var c=(!$.svg.isSVGElem(a)?a.className:(a.className?a.className.baseVal:a.getAttribute('class')));return(' '+c+' ').indexOf(b)>-1};$.expr.filter.ATTR=function(g){return function(c,d){var e=null;if($.svg.isSVGElem(c)){e=d[1];$.expr.attrHandle[e]=function(a){var b=a.getAttribute(e);return b&&b.baseVal||b}}var f=g(c,d);if(e){$.expr.attrHandle[e]=null}return f}}($.expr.filter.ATTR)})(jQuery);(function() {
+(function($){$.fn.addClass=function(e){return function(d){d=d||'';return this.each(function(){if($.svg.isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));if($.inArray(a,b.split(/\s+/))==-1){b+=(b?' ':'')+a;(c.className?c.className.baseVal=b:c.setAttribute('class',b))}})}else{e.apply($(this),[d])}})}}($.fn.addClass);$.fn.removeClass=function(e){return function(d){d=d||'';return this.each(function(){if($.svg.isSVGElem(this)){var c=this;$.each(d.split(/\s+/),function(i,a){var b=(c.className?c.className.baseVal:c.getAttribute('class'));b=$.grep(b.split(/\s+/),function(n,i){return n!=a}).join(' ');(c.className?c.className.baseVal=b:c.setAttribute('class',b))})}else{e.apply($(this),[d])}})}}($.fn.removeClass);$.fn.toggleClass=function(c){return function(a,b){return this.each(function(){if($.svg.isSVGElem(this)){if(typeof b!=='boolean'){b=!$(this).hasClass(a)}$(this)[(b?'add':'remove')+'Class'](a)}else{c.apply($(this),[a,b])}})}}($.fn.toggleClass);$.fn.hasClass=function(d){return function(b){b=b||'';var c=false;this.each(function(){if($.svg.isSVGElem(this)){var a=(this.className?this.className.baseVal:this.getAttribute('class')).split(/\s+/);c=($.inArray(b,a)>-1)}else{c=(d.apply($(this),[b]))}return!c});return c}}($.fn.hasClass);$.fn.attr=function(h){return function(b,c,d){if(typeof b==='string'&&c===undefined){var e=h.apply(this,[b,c,d]);if(e&&e.baseVal&&e.baseVal.numberOfItems!=null){c='';e=e.baseVal;if(b=='transform'){for(var i=0;i']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['>']);$.expr.relative['']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['']);$.expr.relative['~']=function(d){return function(a,b,c){d(a,b,c||anySVG(a))}}($.expr.relative['~']);$.expr.find.ID=function(d){return function(a,b,c){return($.svg.isSVGElem(b)?[b.ownerDocument.getElementById(a[1])]:d(a,b,c))}}($.expr.find.ID);var j=document.createElement('div');j.appendChild(document.createComment(''));if(j.getElementsByTagName('*').length>0){$.expr.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==='*'){var d=[];for(var i=0;c[i]||c.item(i);i++){if((c[i]||c.item(i)).nodeType===1){d.push(c[i]||c.item(i))}}c=d}return c}}$.expr.preFilter.CLASS=function(a,b,c,d,f,g){a=' '+a[1].replace(/\\/g,'')+' ';if(g){return a}for(var i=0,elem={};elem!=null;i++){elem=b[i];if(!elem){try{elem=b.item(i)}catch(e){}}if(elem){var h=(!$.svg.isSVGElem(elem)?elem.className:(elem.className?elem.className.baseVal:'')||elem.getAttribute('class'));if(f^(h&&(' '+h+' ').indexOf(a)>-1)){if(!c)d.push(elem)}else if(c){b[i]=false}}}return false};$.expr.filter.CLASS=function(a,b){var c=(!$.svg.isSVGElem(a)?a.className:(a.className?a.className.baseVal:a.getAttribute('class')));return(' '+c+' ').indexOf(b)>-1};$.expr.filter.ATTR=function(g){return function(c,d){var e=null;if($.svg.isSVGElem(c)){e=d[1];$.expr.attrHandle[e]=function(a){var b=a.getAttribute(e);return b&&b.baseVal||b}}var f=g(c,d);if(e){$.expr.attrHandle[e]=null}return f}}($.expr.filter.ATTR)})(jQuery);// Generated by CoffeeScript 2.5.1
+(function() {
var $;
$ = this.jQuery;
this.magicmockup = (function() {
- var $doc, defaultLayer, filter, init, layers, _dispatch, _findFilters, _getDescription, _getHash, _getInk, _handleClick, _handleHover, _hideLayers, _initLayers, _setInitialPage, _showLayer, _stripInlineJS;
+ var $doc, _dispatch, _findFilters, _getDescription, _getHash, _getInk, _handleClick, _handleHover, _hideLayers, _initLayers, _setInitialPage, _showLayer, _stripInlineJS, defaultLayer, filter, init, layers;
$doc = $(this.document);
layers = {};
filter = {};
defaultLayer = '';
+ // Convenience function to grab attributes from the Inkscape namespace
_getInk = function(el, attr) {
var inkNS;
inkNS = 'http://www.inkscape.org/namespaces/inkscape';
return el.getAttributeNS(inkNS, attr);
};
- _initLayers = function($layers) {
- if ($layers == null) $layers = $('g');
+ // Add each layer to the layer object (if it contains a an Inkscape label)
+ _initLayers = function($layers = $('g')) {
$layers.each(function() {
var group, label;
group = _getInk(this, 'groupmode');
label = _getInk(this, 'label');
if (group === 'layer') {
layers[label] = this;
- if ($(this).is(':visible')) return defaultLayer = label;
+ if ($(this).is(':visible')) {
+ return defaultLayer = label;
+ }
}
});
};
+ // Find all filters and store in the filter object
_findFilters = function() {
return $doc.find('filter').each(function() {
var label;
@@ -9317,65 +9322,73 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
return filter[label] = this.id;
});
};
- _dispatch = function(context, _arg) {
- var act, command, params, val;
- command = _arg[0], val = _arg[1];
+ // Do the heavy lifting
+ // (right now, there's only "next" for switching pages; more to come)
+ _dispatch = function(context, [command, val]) {
+ var act, params;
act = {
load: function(url) {
url = url.shift();
return window.location = url || val;
},
next: function(location) {
- var _base;
+ var base;
location = location.shift();
if (location.match(/#/)) {
+ // if "#" is added, then load the new page
return act.load(location);
} else {
+ // Hide the current visible layer
$(context).parents('g').not('[style=display:none]').last().hide();
- if (typeof (_base = $(layers[location])).show === "function") {
- _base.show();
+ if (typeof (base = $(layers[location])).show === "function") {
+ base.show();
+ }
+ if (location === defaultLayer) {
+ location = '';
}
- if (location === defaultLayer) location = '';
return window.location.hash = location;
}
},
show: function(show_layers) {
- var layer, _i, _len, _results;
- _results = [];
- for (_i = 0, _len = show_layers.length; _i < _len; _i++) {
- layer = show_layers[_i];
- _results.push($(layers[layer]).show());
+ var i, layer, len, results;
+ results = [];
+ for (i = 0, len = show_layers.length; i < len; i++) {
+ layer = show_layers[i];
+ results.push($(layers[layer]).show());
}
- return _results;
+ return results;
},
hide: function(hide_layers) {
- var layer, _i, _len, _results;
- _results = [];
- for (_i = 0, _len = hide_layers.length; _i < _len; _i++) {
- layer = hide_layers[_i];
- _results.push($(layers[layer]).hide());
+ var i, layer, len, results;
+ results = [];
+ for (i = 0, len = hide_layers.length; i < len; i++) {
+ layer = hide_layers[i];
+ results.push($(layers[layer]).hide());
}
- return _results;
+ return results;
},
toggle: function(toggle_layers) {
- var layer, _i, _len, _results;
- _results = [];
- for (_i = 0, _len = toggle_layers.length; _i < _len; _i++) {
- layer = toggle_layers[_i];
- _results.push($(layers[layer]).toggle());
+ var i, layer, len, results;
+ results = [];
+ for (i = 0, len = toggle_layers.length; i < len; i++) {
+ layer = toggle_layers[i];
+ results.push($(layers[layer]).toggle());
}
- return _results;
+ return results;
},
fadeOut: function(params) {
- var easing, layer, time, _ref, _ref2;
+ var easing, layer, ref, ref1, time;
if ((params != null) && params.length > 0) {
+ // Capture parameters with defaults
layer = params[0];
- time = (_ref = params[1]) != null ? _ref : 1;
- easing = (_ref2 = params[2]) != null ? _ref2 : 'linear';
+ time = (ref = params[1]) != null ? ref : 1;
+ easing = (ref1 = params[2]) != null ? ref1 : 'linear';
+ // Convert time from seconds to milliseconds
time = parseInt(time) * 1000;
$(layers[layer]).attr('opacity', 1).animate({
svgOpacity: 0.0
}, time, easing, function() {});
+ // Reset opacity but hide
return $(this).hide().attr('opacity', 1);
}
}
@@ -9383,13 +9396,18 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
params = val != null ? val.split(',') : void 0;
return typeof act[command] === "function" ? act[command](params) : void 0;
};
+ // Return the description for an element
_getDescription = function(el) {
return $(el).children('desc').text();
};
+ // If there's inline JS, strip it (and provide warnings)
_stripInlineJS = function() {
var $onclick;
$onclick = $('[onclick]');
- if (!$onclick.length) return;
+ if (!$onclick.length) {
+ return;
+ }
+ // Warn about inline JS (if console.warn is available)
if (console && console.warn) {
if (typeof console.group === "function") {
console.group('Warning: inline JavaScript found (and deactivated)');
@@ -9397,61 +9415,89 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
$onclick.each(function() {
return console.warn(this.id, ':', this.onclick);
});
- if (typeof console.groupEnd === "function") console.groupEnd();
+ if (typeof console.groupEnd === "function") {
+ console.groupEnd();
+ }
}
+ // Strip the inline JS
$onclick.each(function() {
return this.onclick = void 0;
});
};
+ // Return the URL fragment
_getHash = function() {
return window.location.hash.substr(1);
};
+ // Hide all layers
_hideLayers = function() {
- var layer, name, _results;
- _results = [];
+ var layer, name, results;
+ results = [];
for (name in layers) {
layer = layers[name];
- _results.push($(layer).hide());
+ results.push($(layer).hide());
}
- return _results;
+ return results;
};
+ // Make a layer visible
_showLayer = function(layer) {
- if (typeof layer !== 'string') layer = _getHash();
- if (!(layers[layer] || layer === '')) return;
+ if (typeof layer !== 'string') {
+ layer = _getHash();
+ }
+ // Make sure the layer exists
+ if (!(layers[layer] || layer === '')) {
+ return;
+ }
_hideLayers();
return _dispatch(this, ['next', layer || defaultLayer]);
};
+ // If a hash is specified, view the appropriate layer
_setInitialPage = function() {
var layer;
layer = _getHash();
- if (layer) return _showLayer(layer);
+ if (layer) {
+ return _showLayer(layer);
+ }
};
+ // Handle clicks on items with instructions
_handleClick = function(e) {
- var action, actions, _i, _len, _ref;
+ var action, actions, i, len, ref;
actions = _getDescription(e.currentTarget);
- if (!actions) return;
- _ref = actions.split(/([\s\n]+)/);
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- action = _ref[_i];
+ // Skip if there's no description
+ if (!actions) {
+ return;
+ }
+ ref = actions.split(/([\s\n]+)/);
+ for (i = 0, len = ref.length; i < len; i++) {
+ action = ref[i];
_dispatch(this, action.split(/\=/));
}
};
+ // Change the cursor for interactive elements
_handleHover = function(e) {
var $this, hover, isHovered;
$this = $(this);
isHovered = e.type === "mouseenter";
- if (!_getDescription(e.currentTarget)) return;
+ // Skip if there's no description
+ if (!_getDescription(e.currentTarget)) {
+ return;
+ }
+ // Alter hover CSS if there's a hover filter
if (filter.hover) {
- hover = isHovered ? "url(#" + filter.hover + ")" : "none";
+ hover = isHovered ? `url(#${filter.hover})` : "none";
$this.css({
filter: hover
});
}
- if ($this.data('hoverable')) return;
+ // Skip if already hoverable
+ if ($this.data('hoverable')) {
+ return;
+ }
+ // We're handling the hoverable state now
$this.data('hoverable', true).css({
cursor: 'pointer'
});
};
+ // Run on page load
init = function(loadEvent) {
_initLayers();
_setInitialPage();
@@ -9463,11 +9509,11 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
hover: _handleHover
});
};
- return {
- init: init
- };
+ return {init}; // Public exports
})();
+
+ // Hack to attach the init to for an unobtrusive SVG onload
$('svg').attr({
onload: 'magicmockup.init()'
});