diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index db916e6..4cc2726 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -157,15 +157,15 @@ class Builder { } $pattern = $this->mpl->render($f,$d); + $escaped = htmlentities($pattern); if ($this->addPatternHF) { $patternHead = $this->mv->render($this->patternHead,$d); $patternFoot = $this->mv->render($this->patternFoot,$d); - $patternFoot = str_replace("{% patternHTML %}",htmlentities($pattern),$patternFoot); $pattern = $patternHead.$pattern.$patternFoot; } - return $pattern; + return array($pattern,$escaped); } @@ -246,16 +246,8 @@ class Builder { // make sure this pattern should be rendered if ($pathInfo["render"]) { - $r = $this->generatePatternFile($pathInfo["patternSrcPath"].".mustache",$pathInfo["patternPartial"]); - - // if the pattern directory doesn't exist create it - $path = $pathInfo["patternDestPath"]; - if (!is_dir(__DIR__.$this->pp.$path)) { - mkdir(__DIR__.$this->pp.$path); - file_put_contents(__DIR__.$this->pp.$path."/".$path.".html",$r); - } else { - file_put_contents(__DIR__.$this->pp.$path."/".$path.".html",$r); - } + // get the rendered, escaped, and mustache pattern + $this->generatePatternFile($pathInfo["patternSrcPath"].".mustache",$pathInfo["patternPartial"],$pathInfo["patternDestPath"]); } @@ -266,26 +258,40 @@ class Builder { } /** - * Generates a pattern with a header & footer + * Generates a pattern with a header & footer, the escaped version of a pattern, the msutache template, and the css if appropriate * @param {String} the filename of the file to be rendered * @param {String} the pattern partial - * - * @return {String} the final rendered pattern including the standard header and footer for a pattern + * @param {String} path where the files need to be written too */ - private function generatePatternFile($f,$p) { + private function generatePatternFile($f,$p,$path) { - $rf = $this->renderPattern($f,$p); + // render the pattern and return it as well as the encoded version + list($rf,$e) = $this->renderPattern($f,$p); - // the footer isn't rendered as mustache but we have some variables there any way. find & replace. + // the core footer isn't rendered as mustache but we have some variables there any way. find & replace. $rf = str_replace("{% patternPartial %}",$p,$rf); $rf = str_replace("{% lineage %}",json_encode($this->patternLineages[$p]),$rf); $rf = str_replace("{% lineager %}",json_encode($this->patternLineagesR[$p]),$rf); - if ($this->enableCSS && isset($this->patternCSS[$p])) { - $rf = str_replace("{% patternCSS %}",$this->patternCSS[$p],$rf); + // figure out what to put in the css section + $c = $this->enableCSS && isset($this->patternCSS[$p]) ? "true" : "false"; + $rf = str_replace("{% cssEnabled %}",$c,$rf); + + // get the original mustache template + $m = htmlentities(file_get_contents(__DIR__.$this->sp.$f)); + + // if the pattern directory doesn't exist create it + if (!is_dir(__DIR__.$this->pp.$path)) { + mkdir(__DIR__.$this->pp.$path); } - return $rf; + // write out the various pattern files + file_put_contents(__DIR__.$this->pp.$path."/".$path.".html",$rf); + file_put_contents(__DIR__.$this->pp.$path."/".$path.".escaped.html",$e); + file_put_contents(__DIR__.$this->pp.$path."/".$path.".mustache",$m); + if ($this->enableCSS && isset($this->patternCSS[$p])) { + file_put_contents(__DIR__.$this->pp.$path."/".$path.".css",htmlentities($this->patternCSS[$p])); + } } @@ -799,8 +805,9 @@ class Builder { // add patterns to $this->patternPartials foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItem) { - $patternCodeRaw = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"]); - $patternCodeEncoded = htmlentities($patternCodeRaw); + $patternCode = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"]); + $patternCodeRaw = $patternCode[0]; + $patternCodeEncoded = $patternCode[1]; $patternLineageExists = (count($this->patternLineages[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; $patternLineages = $this->patternLineages[$patternSubtypeItem["patternPartial"]]; diff --git a/core/templates/index.mustache b/core/templates/index.mustache index 8c731ac..1953c48 100644 --- a/core/templates/index.mustache +++ b/core/templates/index.mustache @@ -15,7 +15,7 @@ - + @@ -39,8 +39,57 @@ - - + + + + {{> ipAddress }} {{> patternPaths }} diff --git a/core/templates/pattern-header-footer/footer-pattern.html b/core/templates/pattern-header-footer/footer-pattern.html index 1a426c2..ab89469 100644 --- a/core/templates/pattern-header-footer/footer-pattern.html +++ b/core/templates/pattern-header-footer/footer-pattern.html @@ -4,13 +4,6 @@ var patternPartial = "{% patternPartial %}"; var lineage = {% lineage %}; var lineageR = {% lineager %}; - - - - - \ No newline at end of file diff --git a/core/templates/pattern-header-footer/footer.html b/core/templates/pattern-header-footer/footer.html index 0c083cc..8fdc344 100644 --- a/core/templates/pattern-header-footer/footer.html +++ b/core/templates/pattern-header-footer/footer.html @@ -2,11 +2,22 @@ (function() { if (self != top) { var cb = '{{ cacheBuster}}'; - var js = ["styleguide/js/postmessage.js","data/annotations.js","styleguide/js/annotations-pattern.js","styleguide/js/code-pattern.js"] + var js = [{"src": "styleguide/js/postmessage.js" }, { "src": "data/annotations.js", "dep": [{ "src": "styleguide/js/annotations-pattern.js" }]},{ "src": "styleguide/js/code-pattern.js"}]; var s = document.getElementById('pl-js-insert-{{ cacheBuster }}'); for (var i = 0; i < js.length; i++) { var c = document.createElement('script'); - c.src = '../../'+js[i]+'?'+cb; + c.src = '../../'+js[i].src+'?'+cb; + if (js[i].dep !== undefined) { + c.onload = function(dep) { + return function() { + for (var k = 0; k < dep.length; k++) { + var d = document.createElement('script'); + d.src = '../../'+dep[k].src+'?'+cb; + s.parentNode.insertBefore(d,s); + } + } + }(js[i].dep); + } s.parentNode.insertBefore(c,s); } } diff --git a/public/styleguide/js/code-pattern.js b/public/styleguide/js/code-pattern.js index e85d3d4..65756ed 100644 --- a/public/styleguide/js/code-pattern.js +++ b/public/styleguide/js/code-pattern.js @@ -50,7 +50,7 @@ var codePattern = { if (codePattern.codeOverlayActive) { var targetOrigin = (window.location.protocol == "file:") ? "*" : window.location.protocol+"//"+window.location.host; - var obj = { "codeOverlay": "on", "lineage": lineage, "lineageR": lineageR, "html": document.getElementById("sg-pattern-html").textContent, "css": document.getElementById("sg-pattern-css").textContent }; + var obj = { "codeOverlay": "on", "lineage": lineage, "lineageR": lineageR, "codePatternPartial": patternPartial, "cssEnabled": cssEnabled }; parent.postMessage(obj,targetOrigin); } else if (codePattern.codeEmbeddedActive) { diff --git a/public/styleguide/js/code-viewer.js b/public/styleguide/js/code-viewer.js index e52db9e..915e2f0 100644 --- a/public/styleguide/js/code-viewer.js +++ b/public/styleguide/js/code-viewer.js @@ -9,6 +9,10 @@ var codeViewer = { codeActive: false, + tabActive: "e", + encoded: "", + mustache: "", + css: "", onReady: function() { @@ -54,7 +58,7 @@ var codeViewer = { codeContainerInit: function() { if (document.getElementById("sg-code-container") === null) { - $('
').html('Close

HTML

').appendTo('body').css('bottom',-$(document).outerHeight()); + $('
').html($("#code-template").html()).appendTo('body').css('bottom',-$(document).outerHeight()); } //Close Code View Button @@ -63,14 +67,90 @@ var codeViewer = { return false; }); + //make sure the click events are handled + $('#sg-code-title-html').click(function() { + $('.sg-code-title-active').removeClass('sg-code-title-active'); + $(this).toggleClass("sg-code-title-active"); + codeViewer.swapCode("e"); + }); + + $('#sg-code-title-mustache').click(function() { + $('.sg-code-title-active').removeClass('sg-code-title-active'); + $(this).toggleClass("sg-code-title-active"); + codeViewer.swapCode("m"); + }); + + $('#sg-code-title-css').click(function() { + $('.sg-code-title-active').removeClass('sg-code-title-active'); + $(this).toggleClass("sg-code-title-active"); + codeViewer.swapCode("c"); + }); + + + }, slideCode: function(pos) { $('#sg-code-container').css('bottom',-pos); }, - updateCode: function(lineage,lineageR,html,css) { - + saveEncoded: function() { + codeViewer.encoded = this.responseText; + if (codeViewer.tabActive == "e") { + codeViewer.activateDefaultTab("e",this.responseText); + } + }, + + saveMustache: function() { + codeViewer.mustache = this.responseText; + if (codeViewer.tabActive == "m") { + codeViewer.activateDefaultTab("m",this.responseText); + } + }, + + saveCSS: function() { + $('#sg-code-title-css').css("display","block"); + codeViewer.css = this.responseText; + if (codeViewer.tabActive == "c") { + codeViewer.activateDefaultTab("c",this.responseText); + } + }, + + swapCode: function(type) { + var fill = ""; + var className = (type == "c") ? "css" : "markup"; + $("#sg-code-fill").removeClass().addClass("language-"+className); + if (type == "m") { + fill = codeViewer.mustache; + } else if (type == "e") { + fill = codeViewer.encoded; + } else if (type == "c") { + fill = codeViewer.css; + } + $("#sg-code-fill").html(fill).text(); + codeViewer.tabActive = type; + Prism.highlightElement(document.getElementById("sg-code-fill")); + }, + + activateDefaultTab: function(type,code) { + var typeName = ""; + var className = (type == "c") ? "css" : "markup"; + if (type == "m") { + typeName = "mustache"; + } else if (type == "e") { + typeName = "html"; + } else if (type == "c") { + typeName = "css"; + } + $('.sg-code-title-active').removeClass('sg-code-title-active'); + $('#sg-code-title-'+typeName).addClass('sg-code-title-active'); + $("#sg-code-fill").removeClass().addClass("language-"+className); + $("#sg-code-fill").html(code).text(); + Prism.highlightElement(document.getElementById("sg-code-fill")); + }, + + updateCode: function(lineage,lineageR,patternPartial,cssEnabled) { + // draw lineage var lineageList = ""; $("#sg-code-lineage").css("display","none"); @@ -115,16 +195,23 @@ var codeViewer = { document.getElementById("sg-viewport").contentWindow.postMessage( { "path": urlHandler.getFileName($(this).attr("data-patternpartial")) }, urlHandler.targetOrigin); }); - // draw html - $("#sg-code-html-fill").html(html).text(); - Prism.highlightElement(document.getElementById("sg-code-html-fill")); + var fileName = urlHandler.getFileName(patternPartial); - // draw CSS - if (css.indexOf("{% patternCSS %}") === -1) { - $("#sg-code-html").addClass("with-css"); - $("#sg-code-css").css("display","block"); - $("#sg-code-css-fill").text(css); - Prism.highlightElement(document.getElementById("sg-code-css-fill")); + var e = new XMLHttpRequest(); + e.onload = this.saveEncoded; + e.open("GET", fileName.replace(/\.html/,".escaped.html") + "?" + (new Date()).getTime(), true); + e.send(); + + var m = new XMLHttpRequest(); + m.onload = this.saveMustache; + m.open("GET", fileName.replace(/\.html/,".mustache") + "?" + (new Date()).getTime(), true); + m.send(); + + if (cssEnabled) { + var c = new XMLHttpRequest(); + c.onload = this.saveCSS; + c.open("GET", fileName.replace(/\.html/,".css") + "?" + (new Date()).getTime(), true); + c.send(); } codeViewer.slideCode(0); @@ -145,13 +232,9 @@ var codeViewer = { if (event.data.codeOverlay !== undefined) { if (event.data.codeOverlay === "on") { - - codeViewer.updateCode(event.data.lineage,event.data.lineageR,event.data.html,event.data.css); - + codeViewer.updateCode(event.data.lineage,event.data.lineageR,event.data.codePatternPartial,event.data.cssEnabled); } else { - codeViewer.slideCode($('#sg-code-container').outerHeight()); - } } @@ -169,3 +252,14 @@ $('#sg-viewport').load(function() { document.getElementById('sg-viewport').contentWindow.postMessage({ "codeToggle": "on" },targetOrigin); } }); + +jwerty.key('cmd+a', function (e) { + if (codeViewer.codeActive) { + selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents(document.getElementById("sg-code-fill")); + selection.removeAllRanges(); + selection.addRange(range); + return false; + } +}); diff --git a/public/styleguide/js/jquery.js b/public/styleguide/js/vendor/jquery.js similarity index 100% rename from public/styleguide/js/jquery.js rename to public/styleguide/js/vendor/jquery.js diff --git a/public/styleguide/js/vendor/jwerty.js b/public/styleguide/js/vendor/jwerty.js new file mode 100644 index 0000000..2ba7730 --- /dev/null +++ b/public/styleguide/js/vendor/jwerty.js @@ -0,0 +1,523 @@ +/* + * jwerty - Awesome handling of keyboard events + * + * jwerty is a JS lib which allows you to bind, fire and assert key combination + * strings against elements and events. It normalises the poor std api into + * something easy to use and clear. + * + * This code is licensed under the MIT + * For the full license see: http://keithamus.mit-license.org/ + * For more information see: http://keithamus.github.com/jwerty + * + * @author Keith Cirkel ('keithamus') + * @license http://keithamus.mit-license.org/ + * @copyright Copyright © 2011, Keith Cirkel + * + */ +(function (global, exports) { + + // Helper methods & vars: + var $d = global.document, + $ = (global.jQuery || global.Zepto || global.ender || $d), + $$, // Element selector function + $b, // Event binding function + $f, // Event firing function + ke = 'keydown'; + + function realTypeOf(v, s) { + return (v === null) ? s === 'null' + : (v === undefined) ? s === 'undefined' + : (v.is && v instanceof $) ? s === 'element' + : Object.prototype.toString.call(v).toLowerCase().indexOf(s) > 7; + } + + if ($ === $d) { + $$ = function (selector, context) { + return selector ? $.querySelector(selector, context || $) : $; + }; + $b = function (e, fn) { e.addEventListener(ke, fn, false); }; + $f = function (e, jwertyEv) { + var ret = $d.createEvent('Event'), + i; + + ret.initEvent(ke, true, true); + + for (i in jwertyEv) ret[i] = jwertyEv[i]; + + return (e || $).dispatchEvent(ret); + }; + } else { + $$ = function (selector, context) { return $(selector || $d, context); }; + $b = function (e, fn) { $(e).bind(ke + '.jwerty', fn); }; + $f = function (e, ob) { $(e || $d).trigger($.Event(ke, ob)); }; + } + + // Private + var _modProps = { 16: 'shiftKey', 17: 'ctrlKey', 18: 'altKey', 91: 'metaKey' }; + + // Generate key mappings for common keys that are not printable. + var _keys = { + + // MOD aka toggleable keys + mods: { + // Shift key, ⇧ + '⇧': 16, + shift: 16, + // CTRL key, on Mac: ⌃ + '⌃': 17, + ctrl: 17, + // ALT key, on Mac: ⌥ (Alt) + '⌥': 18, + alt: 18, + option: 18, + // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super) + '⌘': 91, + meta: 91, + cmd: 91, + 'super': 91, + win: 91 + }, + + // Normal keys + keys: { + // Backspace key, on Mac: ⌫ (Backspace) + '⌫': 8, + backspace: 8, + // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ + '⇥': 9, + '⇆': 9, + tab: 9, + // Return key, ↩ + '↩': 13, + 'return': 13, + enter: 13, + '⌅': 13, + // Pause/Break key + 'pause': 19, + 'pause-break': 19, + // Caps Lock key, ⇪ + '⇪': 20, + caps: 20, + 'caps-lock': 20, + // Escape key, on Mac: ⎋, on Windows: Esc + '⎋': 27, + escape: 27, + esc: 27, + // Space key + space: 32, + // Page-Up key, or pgup, on Mac: ↖ + '↖': 33, + pgup: 33, + 'page-up': 33, + // Page-Down key, or pgdown, on Mac: ↘ + '↘': 34, + pgdown: 34, + 'page-down': 34, + // END key, on Mac: ⇟ + '⇟': 35, + end: 35, + // HOME key, on Mac: ⇞ + '⇞': 36, + home: 36, + // Insert key, or ins + ins: 45, + insert: 45, + // Delete key, on Mac: ⌫ (Delete) + del: 46, + 'delete': 46, + + // Left Arrow Key, or ← + '←': 37, + left: 37, + 'arrow-left': 37, + // Up Arrow Key, or ↑ + '↑': 38, + up: 38, + 'arrow-up': 38, + // Right Arrow Key, or → + '→': 39, + right: 39, + 'arrow-right': 39, + // Up Arrow Key, or ↓ + '↓': 40, + down: 40, + 'arrow-down': 40, + + // odities, printing characters that come out wrong: + // Num-Multiply, or * + '*': 106, + star: 106, + asterisk: 106, + multiply: 106, + // Num-Plus or + + '+': 107, + 'plus': 107, + // Num-Subtract, or - + '-': 109, + subtract: 109, + 'num-.': 110, + 'num-period': 110, + 'num-dot': 110, + 'num-full-stop': 110, + 'num-delete': 110, + // Semicolon + ';': 186, + semicolon: 186, + // = or equals + '=': 187, + 'equals': 187, + // Comma, or , + ',': 188, + comma: 188, + //'-': 189, //??? + // Period, or ., or full-stop + '.': 190, + period: 190, + 'full-stop': 190, + // Slash, or /, or forward-slash + '/': 191, + slash: 191, + 'forward-slash': 191, + // Tick, or `, or back-quote + '`': 192, + tick: 192, + 'back-quote': 192, + // Open bracket, or [ + '[': 219, + 'open-bracket': 219, + // Back slash, or \ + '\\': 220, + 'back-slash': 220, + // Close backet, or ] + ']': 221, + 'close-bracket': 221, + // Apostraphe, or Quote, or ' + '\'': 222, + quote: 222, + apostraphe: 222 + } + + }; + + // To minimise code bloat, add all of the 0-9 and NUMPAD 0-9 keys in a loop + var i = 47, + n = 0; + while (++i < 106) { + _keys.keys[n] = i; + _keys.keys['num-' + n] = i + 48; + ++n; + } + + // To minimise code bloat, add all of the F1-F25 keys in a loop + i = 111, + n = 1; + while (++i < 136) { + _keys.keys['f' + n] = i; + ++n; + } + + // To minimise code bloat, add all of the letters of the alphabet in a loop + i = 64; + while (++i < 91) { + _keys.keys[String.fromCharCode(i).toLowerCase()] = i; + } + + function JwertyCode(jwertyCode) { + var i, + c, + n, + z, + keyCombo, + optionals, + jwertyCodeFragment, + rangeMatches, + rangeI; + + // In-case we get called with an instance of ourselves, just return that. + if (jwertyCode instanceof JwertyCode) return jwertyCode; + + // If jwertyCode isn't an array, cast it as a string and split into array. + if (!realTypeOf(jwertyCode, 'array')) { + jwertyCode = (String(jwertyCode)).replace(/\s/g, '').toLowerCase() + .match(/(?:\+,|[^,])+/g); + } + + // Loop through each key sequence in jwertyCode + for (i = 0, c = jwertyCode.length; i < c; ++i) { + + // If the key combo at this part of the sequence isn't an array, + // cast as a string and split into an array. + if (!realTypeOf(jwertyCode[i], 'array')) { + jwertyCode[i] = String(jwertyCode[i]) + .match(/(?:\+\/|[^\/])+/g); + } + + // Parse the key optionals in this sequence + optionals = [], + n = jwertyCode[i].length; + while (n--) { + + // Begin creating the object for this key combo + jwertyCodeFragment = jwertyCode[i][n]; + + keyCombo = { + jwertyCombo: String(jwertyCodeFragment), + shiftKey: false, + ctrlKey: false, + altKey: false, + metaKey: false + }; + + // If jwertyCodeFragment isn't an array then cast as a string + // and split it into one. + if (!realTypeOf(jwertyCodeFragment, 'array')) { + jwertyCodeFragment = String(jwertyCodeFragment).toLowerCase() + .match(/(?:(?:[^\+])+|\+\+|^\+$)/g); + } + + z = jwertyCodeFragment.length; + while (z--) { + + // Normalise matching errors + if (jwertyCodeFragment[z] === '++') jwertyCodeFragment[z] = '+'; + + // Inject either keyCode or ctrl/meta/shift/altKey into keyCombo + if (jwertyCodeFragment[z] in _keys.mods) { + keyCombo[_modProps[_keys.mods[jwertyCodeFragment[z]]]] = true; + } else if (jwertyCodeFragment[z] in _keys.keys) { + keyCombo.keyCode = _keys.keys[jwertyCodeFragment[z]]; + } else { + rangeMatches = jwertyCodeFragment[z].match(/^\[([^-]+\-?[^-]*)-([^-]+\-?[^-]*)\]$/); + } + } + if (realTypeOf(keyCombo.keyCode, 'undefined')) { + // If we picked up a range match earlier... + if (rangeMatches && (rangeMatches[1] in _keys.keys) && (rangeMatches[2] in _keys.keys)) { + rangeMatches[2] = _keys.keys[rangeMatches[2]]; + rangeMatches[1] = _keys.keys[rangeMatches[1]]; + + // Go from match 1 and capture all key-comobs up to match 2 + for (rangeI = rangeMatches[1]; rangeI < rangeMatches[2]; ++rangeI) { + optionals.push({ + altKey: keyCombo.altKey, + shiftKey: keyCombo.shiftKey, + metaKey: keyCombo.metaKey, + ctrlKey: keyCombo.ctrlKey, + keyCode: rangeI, + jwertyCombo: String(jwertyCodeFragment) + }); + + } + keyCombo.keyCode = rangeI; + // Inject either keyCode or ctrl/meta/shift/altKey into keyCombo + } else { + keyCombo.keyCode = 0; + } + } + optionals.push(keyCombo); + + } + this[i] = optionals; + } + this.length = i; + return this; + } + + var jwerty = exports.jwerty = { + /** + * jwerty.event + * + * `jwerty.event` will return a function, which expects the first + * argument to be a key event. When the key event matches `jwertyCode`, + * `callbackFunction` is fired. `jwerty.event` is used by `jwerty.key` + * to bind the function it returns. `jwerty.event` is useful for + * attaching to your own event listeners. It can be used as a decorator + * method to encapsulate functionality that you only want to fire after + * a specific key combo. If `callbackContext` is specified then it will + * be supplied as `callbackFunction`'s context - in other words, the + * keyword `this` will be set to `callbackContext` inside the + * `callbackFunction` function. + * + * @param {Mixed} jwertyCode can be an array, or string of key + * combinations, which includes optinals and or sequences + * @param {Function} callbackFucntion is a function (or boolean) which + * is fired when jwertyCode is matched. Return false to + * preventDefault() + * @param {Object} callbackContext (Optional) The context to call + * `callback` with (i.e this) + * + */ + event: function (jwertyCode, callbackFunction, callbackContext /*? this */) { + + // Construct a function out of callbackFunction, if it is a boolean. + if (realTypeOf(callbackFunction, 'boolean')) { + var bool = callbackFunction; + callbackFunction = function () { return bool; }; + } + + jwertyCode = new JwertyCode(jwertyCode); + + // Initialise in-scope vars. + var i = 0, + c = jwertyCode.length - 1, + returnValue, + jwertyCodeIs; + + // This is the event listener function that gets returned... + return function (event) { + + // if jwertyCodeIs returns truthy (string)... + if ((jwertyCodeIs = jwerty.is(jwertyCode, event, i))) { + // ... and this isn't the last key in the sequence, + // incriment the key in sequence to check. + if (i < c) { + ++i; + return; + // ... and this is the last in the sequence (or the only + // one in sequence), then fire the callback + } else { + returnValue = callbackFunction.call( + callbackContext || this, event, jwertyCodeIs); + + // If the callback returned false, then we should run + // preventDefault(); + if (returnValue === false) event.preventDefault(); + + // Reset i for the next sequence to fire. + i = 0; + return; + } + } + + // If the event didn't hit this time, we should reset i to 0, + // that is, unless this combo was the first in the sequence, + // in which case we should reset i to 1. + i = jwerty.is(jwertyCode, event) ? 1 : 0; + }; + }, + + /** + * jwerty.is + * + * `jwerty.is` will return a boolean value, based on if `event` matches + * `jwertyCode`. `jwerty.is` is called by `jwerty.event` to check + * whether or not to fire the callback. `event` can be a DOM event, or + * a jQuery/Zepto/Ender manufactured event. The properties of + * `jwertyCode` (speficially ctrlKey, altKey, metaKey, shiftKey and + * keyCode) should match `jwertyCode`'s properties - if they do, then + * `jwerty.is` will return `true`. If they don't, `jwerty.is` will + * return `false`. + * + * @param {Mixed} jwertyCode can be an array, or string of key + * combinations, which includes optinals and or sequences + * @param {KeyboardEvent} event is the KeyboardEvent to assert against + * @param {Integer} i (Optional) checks the `i` key in jwertyCode + * sequence + * + */ + is: function (jwertyCode, event, i /*? 0*/) { + jwertyCode = new JwertyCode(jwertyCode); + // Default `i` to 0 + i = i || 0; + // We are only interested in `i` of jwertyCode; + jwertyCode = jwertyCode[i]; + // jQuery stores the *real* event in `originalEvent`, which we use + // because it does annoything stuff to `metaKey` + event = event.originalEvent || event; + + // We'll look at each optional in this jwertyCode sequence... + var n = jwertyCode.length, + returnValue = false; + + // Loop through each fragment of jwertyCode + while (n--) { + returnValue = jwertyCode[n].jwertyCombo; + // For each property in the jwertyCode object, compare to `event` + for (var p in jwertyCode[n]) { + // ...except for jwertyCode.jwertyCombo... + if (p !== 'jwertyCombo' && event[p] != jwertyCode[n][p]) returnValue = false; + } + // If this jwertyCode optional wasn't falsey, then we can return early. + if (returnValue !== false) return returnValue; + } + return returnValue; + }, + + /** + * jwerty.key + * + * `jwerty.key` will attach an event listener and fire + * `callbackFunction` when `jwertyCode` matches. The event listener is + * attached to `document`, meaning it will listen for any key events + * on the page (a global shortcut listener). If `callbackContext` is + * specified then it will be supplied as `callbackFunction`'s context + * - in other words, the keyword `this` will be set to + * `callbackContext` inside the `callbackFunction` function. + * + * @param {Mixed} jwertyCode can be an array, or string of key + * combinations, which includes optinals and or sequences + * @param {Function} callbackFunction is a function (or boolean) which + * is fired when jwertyCode is matched. Return false to + * preventDefault() + * @param {Object} callbackContext (Optional) The context to call + * `callback` with (i.e this) + * @param {Mixed} selector can be a string, jQuery/Zepto/Ender object, + * or an HTML*Element on which to bind the eventListener + * @param {Mixed} selectorContext can be a string, jQuery/Zepto/Ender + * object, or an HTML*Element on which to scope the selector + * + */ + key: function (jwertyCode, callbackFunction, callbackContext /*? this */, selector /*? document */, selectorContext /*? body */) { + // Because callbackContext is optional, we should check if the + // `callbackContext` is a string or element, and if it is, then the + // function was called without a context, and `callbackContext` is + // actually `selector` + var realSelector = realTypeOf(callbackContext, 'element') || realTypeOf(callbackContext, 'string') ? callbackContext : selector, + // If `callbackContext` is undefined, or if we skipped it (and + // therefore it is `realSelector`), set context to `global`. + realcallbackContext = realSelector === callbackContext ? global : callbackContext, + // Finally if we did skip `callbackContext`, then shift + // `selectorContext` to the left (take it from `selector`) + realSelectorContext = realSelector === callbackContext ? selector : selectorContext; + + // If `realSelector` is already a jQuery/Zepto/Ender/DOM element, + // then just use it neat, otherwise find it in DOM using $$() + $b( + realTypeOf(realSelector, 'element') ? realSelector : $$(realSelector, realSelectorContext), + jwerty.event(jwertyCode, callbackFunction, realcallbackContext) + ); + }, + + /** + * jwerty.fire + * + * `jwerty.fire` will construct a keyup event to fire, based on + * `jwertyCode`. The event will be fired against `selector`. + * `selectorContext` is used to search for `selector` within + * `selectorContext`, similar to jQuery's + * `$('selector', 'context')`. + * + * @param {Mixed} jwertyCode can be an array, or string of key + * combinations, which includes optinals and or sequences + * @param {Mixed} selector can be a string, jQuery/Zepto/Ender object, + * or an HTML*Element on which to bind the eventListener + * @param {Mixed} selectorContext can be a string, jQuery/Zepto/Ender + * object, or an HTML*Element on which to scope the selector + * + */ + fire: function (jwertyCode, selector /*? document */, selectorContext /*? body */, i) { + jwertyCode = new JwertyCode(jwertyCode); + var realI = realTypeOf(selectorContext, 'number') ? selectorContext : i; + + // If `realSelector` is already a jQuery/Zepto/Ender/DOM element, + // then just use it neat, otherwise find it in DOM using $$() + $f( + realTypeOf(selector, 'element') ? selector : $$(selector, selectorContext), + jwertyCode[realI || 0][0] + ); + }, + + KEYS: _keys + }; + +}(this, (typeof module !== 'undefined' && module.exports ? module.exports : this))); \ No newline at end of file diff --git a/public/styleguide/js/prism.js b/public/styleguide/js/vendor/prism.js similarity index 100% rename from public/styleguide/js/prism.js rename to public/styleguide/js/vendor/prism.js