diff --git a/Gruntfile.js b/Gruntfile.js index ad4f48ca2..22156a0f8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -76,6 +76,8 @@ module.exports = function(grunt) { files: [ 'source/_patterns/**/*.mustache', 'source/_patterns/**/*.json', + 'source/_patterns/**/*.css', + 'source/_patterns/**/*.js', 'source/_data/*.json' ], tasks: ['default'] diff --git a/builder/patternlab.js b/builder/patternlab.js index fc331d124..a4f83b17a 100644 --- a/builder/patternlab.js +++ b/builder/patternlab.js @@ -8,7 +8,7 @@ * */ - var patternlab_engine = function(){ +var patternlab_engine = function(){ var path = require('path'), fs = require('fs-extra'), extend = require('util')._extend, @@ -78,8 +78,10 @@ var subdir = path.dirname(path.relative('./source/_patterns', file)).replace('\\', '/'); var filename = path.basename(file); - //ignore _underscored patterns, json (for now), and dotfiles - if(filename.charAt(0) === '_' || path.extname(filename) === '.json' || filename.charAt(0) === '.'){ + //ignore _underscored patterns, json/css/js (for now), and dotfiles + if(filename.charAt(0) === '_' || + path.extname(filename) === '.json' || path.extname(filename) === '.css' || path.extname(filename) === '.js' || + filename.charAt(0) === '.'){ return; } @@ -102,6 +104,24 @@ } currentPattern.template = fs.readFileSync(abspath, 'utf8'); + //look for a css file for this template + var cssPath = abspath.substr(0, abspath.lastIndexOf(".")) + ".css"; + var cssExists = fs.existsSync(cssPath); + + if (cssExists) { + currentPattern.cssExists = true; + currentPattern.css = fs.readFileSync(cssPath, 'utf8'); + } + + //look for a js file for this template + var jsPath = abspath.substr(0, abspath.lastIndexOf(".")) + ".js"; + var jsExists = fs.existsSync(jsPath); + + if (jsExists) { + currentPattern.jsExists = true; + currentPattern.js = fs.readFileSync(jsPath, 'utf8'); + } + //find pattern lineage var lineage_hunter = new lh(); lineage_hunter.find_lineage(currentPattern, patternlab); @@ -185,6 +205,16 @@ var entity_encoder = new he(); //write the encoded version too fs.outputFileSync('./public/patterns/' + pattern.patternLink.replace('.html', '.escaped.html'), entity_encoder.encode(pattern.patternPartial)); + + //write the css file too + if(pattern.cssExists){ + fs.outputFileSync('./public/patterns/' + pattern.patternLink.replace('.html', '.css'), pattern.css); + } + + //write the js file too + if(pattern.jsExists){ + fs.outputFileSync('./public/patterns/' + pattern.patternLink.replace('.html', '.js'), pattern.js); + } }); //export patterns if necessary diff --git a/public/styleguide/js/code-pattern.js b/public/styleguide/js/code-pattern.js index f8a949519..6ac641841 100644 --- a/public/styleguide/js/code-pattern.js +++ b/public/styleguide/js/code-pattern.js @@ -7,40 +7,40 @@ */ var codePattern = { - + codeOverlayActive: false, codeEmbeddedActive: false, targetOrigin: (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host, - + /** * toggle the annotation feature on/off * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage * @param {Object} event info */ receiveIframeMessage: function(event) { - + var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - + // does the origin sending the message match the current host? if not dev/null the request if ((window.location.protocol != "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { return; } - + if (data.codeToggle !== undefined) { - + var els, i; - + // if this is an overlay make sure it's active for the onclick event codePattern.codeOverlayActive = false; codePattern.codeEmbeddedActive = false; - + // see which flag to toggle based on if this is a styleguide or view-all page if ((data.codeToggle == "on") && (document.getElementById("sg-patterns") !== null)) { codePattern.codeEmbeddedActive = true; } else if (data.codeToggle == "on") { codePattern.codeOverlayActive = true; } - + // if comments embedding is turned off make sure to hide the annotations div if (!codePattern.codeEmbeddedActive && (document.getElementById("sg-patterns") !== null)) { els = document.getElementsByClassName("sg-code"); @@ -48,27 +48,27 @@ var codePattern = { els[i].style.display = "none"; } } - + // if comments overlay is turned on add the has-comment class and pointer if (codePattern.codeOverlayActive) { - - var obj = JSON.stringify({ "codeOverlay": "on", "lineage": lineage, "lineageR": lineageR, "patternPartial": patternPartial, "patternState": patternState, "cssEnabled": cssEnabled }); + + var obj = JSON.stringify({ "codeOverlay": "on", "lineage": lineage, "lineageR": lineageR, "patternPartial": patternPartial, "patternState": patternState, "cssEnabled": cssEnabled, "jsEnabled": jsEnabled }); parent.postMessage(obj,codePattern.targetOrigin); - + } else if (codePattern.codeEmbeddedActive) { - + // if code embedding is turned on simply display them els = document.getElementsByClassName("sg-code"); for (i = 0; i < els.length; ++i) { els[i].style.display = "block"; } - + } - + } - + } - + }; // add the onclick handlers to the elements that have an annotations diff --git a/public/styleguide/js/code-viewer.js b/public/styleguide/js/code-viewer.js index ede15941f..cd91ddf3c 100644 --- a/public/styleguide/js/code-viewer.js +++ b/public/styleguide/js/code-viewer.js @@ -7,22 +7,23 @@ */ var codeViewer = { - + // set up some defaults codeActive: false, tabActive: "e", encoded: "", mustache: "", css: "", - ids: { "e": "#sg-code-title-html", "m": "#sg-code-title-mustache", "c": "#sg-code-title-css" }, + js: "", + ids: { "e": "#sg-code-title-html", "m": "#sg-code-title-mustache", "c": "#sg-code-title-css", "j": "#sg-code-title-js" }, targetOrigin: (window.location.protocol === "file:") ? "*" : window.location.protocol+"//"+window.location.host, copyOnInit: false, - + /** * add the onclick handler to the code link in the main nav */ onReady: function() { - + // not sure this is needed anymore... $('body').addClass('code-ready'); @@ -31,71 +32,71 @@ var codeViewer = { codeViewer.slideCode($('#sg-code-container').outerHeight()); } }); - + // add the onclick handler $('#sg-t-code').click(function(e) { - + e.preventDefault(); - + // remove the class from the "eye" nav item $('#sg-t-toggle').removeClass('active'); - + // if the code link in the main nav is active close the panel. otherwise open if ($(this).hasClass('active')) { codeViewer.closeCode(); } else { codeViewer.openCode(); } - + }); - + // initialize the code viewer codeViewer.codeContainerInit(); - + // load the query strings in case code view has to show by default var queryStringVars = urlHandler.getRequestVars(); if ((queryStringVars.view !== undefined) && ((queryStringVars.view === "code") || (queryStringVars.view === "c"))) { codeViewer.copyOnInit = ((queryStringVars.copy !== undefined) && (queryStringVars.copy === "true")) ? true : false; codeViewer.openCode(); } - + }, - + /** * decide on if the code panel should be open or closed */ toggleCode: function() { - + if (!codeViewer.codeActive) { codeViewer.openCode(); } else { codeViewer.closeCode(); } - + }, - + /** * after clicking the code view link open the panel */ openCode: function() { - + // make sure the annotations overlay is off before showing code view $('#sg-t-annotations').removeClass('active'); annotationsViewer.commentsActive = false; var obj = JSON.stringify({ "commentToggle": "off" }); document.getElementById('sg-viewport').contentWindow.postMessage(obj,codeViewer.targetOrigin); annotationsViewer.slideComment(999); - + // tell the iframe code view has been turned on var obj = JSON.stringify({ "codeToggle": "on" }); document.getElementById('sg-viewport').contentWindow.postMessage(obj,codeViewer.targetOrigin); - + // note it's turned on in the viewer codeViewer.codeActive = true; $('#sg-t-code').addClass('active'); - + }, - + /** * after clicking the code view link close the panel */ @@ -106,49 +107,65 @@ var codeViewer = { codeViewer.slideCode($('#sg-code-container').outerHeight()); $('#sg-t-code').removeClass('active'); }, - + /** * add the basic mark-up and events for the code container */ codeContainerInit: function() { - + // the bulk of this template is in core/templates/index.mustache if (document.getElementById("sg-code-container") === null) { $('
').html($("#code-template").html()).appendTo('body').css('bottom',-$(document).outerHeight()); setTimeout(function(){ $('#sg-code-container').addClass('anim-ready'); },50); //Add animation class once container is positioned out of frame } - + // make sure the close button handles the click $('body').delegate('#sg-code-close-btn','click',function() { codeViewer.closeCode(); return false; }); - + // make sure the click events are handled on the HTML tab $(codeViewer.ids["e"]).click(function() { codeViewer.swapCode("e"); }); - + // make sure the click events are handled on the Mustache tab $(codeViewer.ids["m"]).click(function() { codeViewer.swapCode("m"); }); - + // make sure the click events are handled on the CSS tab $(codeViewer.ids["c"]).click(function() { codeViewer.swapCode("c"); }); - + + // make sure the click events are handled on the JS tab + $(codeViewer.ids["j"]).click(function() { + codeViewer.swapCode("j"); + }); + }, - + /** * depending on what tab is clicked this swaps out the code container. makes sure prism highlight is added. */ swapCode: function(type) { - + codeViewer.clearSelection(); var fill = ""; - var className = (type == "c") ? "css" : "markup"; + var className; + switch(type){ + case "css": + className = "css"; + break; + case "js": + className = "js"; + break; + default: + className = "markup"; + break; + } $("#sg-code-fill").removeClass().addClass("language-"+className); if (type == "m") { fill = codeViewer.mustache; @@ -156,6 +173,8 @@ var codeViewer = { fill = codeViewer.encoded; } else if (type == "c") { fill = codeViewer.css; + } else if (type == "j") { + fill = codeViewer.js; } $("#sg-code-fill").html(fill).text(); codeViewer.tabActive = type; @@ -163,7 +182,7 @@ var codeViewer = { $('.sg-code-title-active').removeClass('sg-code-title-active'); $(codeViewer.ids[type]).toggleClass("sg-code-title-active"); }, - + /** * select the code where using cmd+a/ctrl+a */ @@ -176,7 +195,7 @@ var codeViewer = { selection.addRange(range); } }, - + /** * clear any selection of code when swapping tabs or opening a new pattern */ @@ -189,14 +208,14 @@ var codeViewer = { } } }, - + /** * slides the panel */ slideCode: function(pos) { $('#sg-code-container').css('bottom',-pos); }, - + /** * once the AJAX request for the encoded mark-up is finished this runs. * if the encoded tab is the current active tab it adds the content to the default code container @@ -207,7 +226,7 @@ var codeViewer = { codeViewer.activateDefaultTab("e",this.responseText); } }, - + /** * once the AJAX request for the mustache mark-up is finished this runs. * if the mustache tab is the current active tab it adds the content to the default code container @@ -218,7 +237,7 @@ var codeViewer = { codeViewer.activateDefaultTab("m",this.responseText); } }, - + /** * once the AJAX request for the css mark-up is finished this runs. if this function is running then css has been enabled * if the css tab is the current active tab it adds the content to the default code container @@ -230,19 +249,68 @@ var codeViewer = { codeViewer.activateDefaultTab("c",this.responseText); } }, - + + /** + * if the CSS is not provided for the pattern- hides the CSS tab + * if the CSS tab is the current active tab function changes it to the HTML + */ + cleanCss: function() { + $('#sg-code-title-css').css("display","none"); + codeViewer.css = ""; + if (codeViewer.tabActive == "c") { + codeViewer.tabActive = "e"; + } + }, + + /** + * once the AJAX request for the js mark-up is finished this runs. if this function is running then js has been enabled + * if the js tab is the current active tab it adds the content to the default code container + */ + saveJS: function() { + $('#sg-code-title-js').css("display","block"); + codeViewer.js = this.responseText; + if (codeViewer.tabActive == "j") { + codeViewer.activateDefaultTab("j",this.responseText); + } + }, + + /** + * if the JS is not provided for the pattern- hides the JS tab + * if the JS tab is the current active tab function changes it to the HTML + */ + cleanJs: function() { + $('#sg-code-title-js').css("display","none"); + codeViewer.js = ""; + if (codeViewer.tabActive == "j") { + codeViewer.tabActive = "e"; + } + }, + /** * when loading the code view make sure the active tab is highlighted and filled in appropriately */ activateDefaultTab: function(type,code) { var typeName = ""; - var className = (type == "c") ? "css" : "markup"; + var className; + switch(type){ + case "css": + className = "css"; + break; + case "js": + className = "js"; + break; + default: + className = "markup"; + break; + } if (type == "m") { typeName = "mustache"; } else if (type == "e") { typeName = "html"; } else if (type == "c") { typeName = "css"; + } else if (type == "j") { + typeName = "js"; } $('.sg-code-title-active').removeClass('sg-code-title-active'); $('#sg-code-title-'+typeName).addClass('sg-code-title-active'); @@ -254,16 +322,16 @@ var codeViewer = { codeViewer.copyOnInit = false; } }, - + /** * when turning on or switching between patterns with code view on make sure we get * the code from from the pattern via post message */ - updateCode: function(lineage,lineageR,patternPartial,patternState,cssEnabled) { - + updateCode: function(lineage,lineageR,patternPartial,patternState,cssEnabled, jsEnabled) { + // clear any selections that might have been made codeViewer.clearSelection(); - + // draw lineage if (lineage.length !== 0) { var lineageList = ""; @@ -277,7 +345,7 @@ var codeViewer = { } else { $("#sg-code-lineage").css("display","none"); } - + // draw reverse lineage if (lineageR.length !== 0) { var lineageRList = ""; @@ -291,7 +359,7 @@ var codeViewer = { } else { $("#sg-code-lineager").css("display","none"); } - + // when clicking on a lineage item change the iframe source $('#sg-code-lineage-fill a, #sg-code-lineager-fill a').on("click", function(e){ e.preventDefault(); @@ -299,7 +367,7 @@ var codeViewer = { var obj = JSON.stringify({ "path": urlHandler.getFileName($(this).attr("data-patternpartial")) }); document.getElementById("sg-viewport").contentWindow.postMessage(JSON.parse(obj),codeViewer.targetOrigin); }); - + // show pattern state if (patternState != "") { $("#sg-code-patternstate").css("display","block"); @@ -308,58 +376,70 @@ var codeViewer = { } else { $("#sg-code-patternstate").css("display","none"); } - + // fill in the name of the pattern $('#sg-code-lineage-patternname, #sg-code-lineager-patternname, #sg-code-patternstate-patternname').html(patternPartial); - + // get the file name of the pattern so we can get the various editions of the code that can show in code view var fileName = urlHandler.getFileName(patternPartial); - + // request the encoded markup version of the pattern var e = new XMLHttpRequest(); e.onload = this.saveEncoded; e.open("GET", fileName.replace(/\.html/,".escaped.html") + "?" + (new Date()).getTime(), true); e.send(); - + // request the mustache markup version of the pattern var m = new XMLHttpRequest(); m.onload = this.saveMustache; m.open("GET", fileName.replace(/\.html/,".mustache") + "?" + (new Date()).getTime(), true); m.send(); - + // if css is enabled request the css for the pattern if (cssEnabled) { var c = new XMLHttpRequest(); c.onload = this.saveCSS; c.open("GET", fileName.replace(/\.html/,".css") + "?" + (new Date()).getTime(), true); c.send(); + } else{ + this.cleanCss(); + } + + // if js is enabled request the js for the pattern + if (jsEnabled) { + var j = new XMLHttpRequest(); + j.onload = this.saveJS; + j.open("GET", fileName.replace(/\.html/,".js") + "?" + (new Date()).getTime(), true); + j.send(); + } else{ + this.cleanJs(); } - + // move the code into view codeViewer.slideCode(0); - + $("#sg-code-loader").css("display","none"); - + }, - + /** * toggle the comment pop-up based on a user clicking on the pattern * based on the great MDN docs at https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage * @param {Object} event info */ receiveIframeMessage: function(event) { - + var data = (typeof event.data !== "string") ? event.data : JSON.parse(event.data); - + // does the origin sending the message match the current host? if not dev/null the request if ((window.location.protocol !== "file:") && (event.origin !== window.location.protocol+"//"+window.location.host)) { return; } - + // switch based on stuff related to the postmessage if (data.codeOverlay !== undefined) { if (data.codeOverlay === "on") { - codeViewer.updateCode(data.lineage,data.lineageR,data.patternPartial,data.patternState,data.cssEnabled); + codeViewer.updateCode(data.lineage,data.lineageR,data.patternPartial,data.patternState,data.cssEnabled, data.jsEnabled); } else { codeViewer.slideCode($('#sg-code-container').outerHeight()); } @@ -387,9 +467,9 @@ var codeViewer = { } } } - + } - + }; // when the document is ready make the codeViewer ready diff --git a/source/_patternlab-files/index.mustache b/source/_patternlab-files/index.mustache index 1688121dd..e910125f5 100644 --- a/source/_patternlab-files/index.mustache +++ b/source/_patternlab-files/index.mustache @@ -63,6 +63,7 @@
  • HTML
  • Mustache
  • +
    diff --git a/source/_patternlab-files/pattern-header-footer/footer.html b/source/_patternlab-files/pattern-header-footer/footer.html index f66f45b92..1cd946252 100644 --- a/source/_patternlab-files/pattern-header-footer/footer.html +++ b/source/_patternlab-files/pattern-header-footer/footer.html @@ -11,8 +11,9 @@ var patternPartial = "{{ patternGroup }}-{{ patternName }}"; var lineage = [{{{ lineage }}}]; var lineageR = [{{{ lineageR }}}]; - var patternState = "{{patternState}}" - var cssEnabled = false; //TODO + var patternState = "{{patternState}}"; + var cssEnabled = ("{{cssExists}}" === "true"); + var jsEnabled = ("{{jsExists}}" === "true");