├── vendor ├── sarge │ ├── .gitignore │ ├── Rakefile │ ├── test │ │ ├── jsTestDriver.conf │ │ └── tests │ │ │ └── sarge_test.js │ ├── Jsfile │ ├── README.textile │ ├── doc │ │ └── api.txt │ └── sarge.js ├── sarge-eventually │ ├── .gitmodules │ ├── test │ │ ├── jsTestDriver.conf │ │ └── tests │ │ │ └── sarge-eventually_test.js │ ├── README.textile │ └── sarge-eventually.js ├── copperplate │ ├── test │ │ ├── jsTestDriver.conf │ │ └── tests │ │ │ └── copperplate_test.js │ ├── README.textile │ └── copperplate.js └── sarge-standards-css-select │ ├── README.textile │ └── sarge-standards-css-select.js ├── public ├── js │ ├── lib │ │ ├── sarge.js │ │ ├── js-epub.js │ │ ├── js-unzip.js │ │ ├── js-inflate.js │ │ ├── copperplate.js │ │ ├── sarge-eventually.js │ │ └── sarge-standards-css-select.js │ ├── booktorious │ │ ├── reader.js │ │ ├── loading.js │ │ └── spine_entry.js │ ├── booktorious.js │ ├── application.js │ └── sarge-fu.js ├── screen.css └── index.html ├── README └── .gitmodules /vendor/sarge/.gitignore: -------------------------------------------------------------------------------- 1 | test/lib 2 | vendor -------------------------------------------------------------------------------- /public/js/lib/sarge.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/sarge/sarge.js -------------------------------------------------------------------------------- /public/js/lib/js-epub.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/js-epub/js-epub.js -------------------------------------------------------------------------------- /public/js/lib/js-unzip.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/js-unzip/js-unzip.js -------------------------------------------------------------------------------- /public/js/lib/js-inflate.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/js-inflate/js-inflate.js -------------------------------------------------------------------------------- /public/js/lib/copperplate.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/copperplate/copperplate.js -------------------------------------------------------------------------------- /public/js/lib/sarge-eventually.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/sarge-eventually/sarge-eventually.js -------------------------------------------------------------------------------- /public/js/lib/sarge-standards-css-select.js: -------------------------------------------------------------------------------- 1 | ../../../vendor/sarge-standards-css-select/sarge-standards-css-select.js -------------------------------------------------------------------------------- /vendor/sarge-eventually/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/sarge"] 2 | path = vendor/sarge 3 | url = git://github.com/augustl/sarge.git 4 | -------------------------------------------------------------------------------- /vendor/copperplate/test/jsTestDriver.conf: -------------------------------------------------------------------------------- 1 | server: http://localhost:4224 2 | 3 | load: 4 | - ../copperplate.js 5 | - tests/*_test.js 6 | -------------------------------------------------------------------------------- /vendor/sarge/Rakefile: -------------------------------------------------------------------------------- 1 | desc "Generates the documentation in doc/." 2 | task :generate_doc do 3 | exec("asciidoc -a toc doc/api.txt") 4 | end 5 | -------------------------------------------------------------------------------- /vendor/sarge/test/jsTestDriver.conf: -------------------------------------------------------------------------------- 1 | server: http://localhost:4224 2 | 3 | load: 4 | - ../sarge.js 5 | - lib/*.js 6 | - tests/*_test.js 7 | -------------------------------------------------------------------------------- /vendor/sarge-standards-css-select/README.textile: -------------------------------------------------------------------------------- 1 | h2. Sarge standards css select 2 | 3 | Adds CSS selectors support to sarge, via the standards document.querySelectorAll. -------------------------------------------------------------------------------- /vendor/sarge-eventually/test/jsTestDriver.conf: -------------------------------------------------------------------------------- 1 | server: http://localhost:4224 2 | 3 | load: 4 | - ../vendor/sarge/sarge.js 5 | - ../sarge-eventually.js 6 | - tests/*_test.js 7 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | HTML5 EPUB reader proof of concept. 2 | 3 | To get started, run a HTTP server with public/ as document root. Or use file:// to open public/index.html, but that won't work in Chrome. -------------------------------------------------------------------------------- /vendor/sarge-eventually/README.textile: -------------------------------------------------------------------------------- 1 | h2. sarge-eventually 2 | 3 | Adds basic event binding and event triggering to "sarge":http://github.com/augustl/sarge. 4 | 5 | Tested on IE6-8, Opera 10, Firefox 3.0-3.6, Chrome 5, Safari 5 -------------------------------------------------------------------------------- /vendor/sarge-standards-css-select/sarge-standards-css-select.js: -------------------------------------------------------------------------------- 1 | sarge.api.cssSelect = function (selector, rootNode) { 2 | var nodes = (rootNode || document).querySelectorAll(selector); 3 | return Array.prototype.slice.call(nodes); 4 | }; -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/js-epub"] 2 | path = vendor/js-epub 3 | url = git://github.com/augustl/js-epub.git 4 | [submodule "vendor/js-unzip"] 5 | path = vendor/js-unzip 6 | url = git://github.com/augustl/js-unzip.git 7 | [submodule "vendor/js-inflate"] 8 | path = vendor/js-inflate 9 | url = git://github.com/augustl/js-inflate.git -------------------------------------------------------------------------------- /vendor/sarge/Jsfile: -------------------------------------------------------------------------------- 1 | # My own unreleased dependency manager. Sorry. Will release it soon. 2 | 3 | group :test do 4 | path "test/lib" 5 | 6 | git "git://gitorious.org/sinon/sinon.git", 7 | :files => ["src/sinon.js", "src/sinon/spy.js", "src/sinon/mock.js", "src/sinon/stub.js", "src/sinon/*.js"], 8 | :out => "sinon.js" 9 | end 10 | 11 | -------------------------------------------------------------------------------- /public/js/booktorious/reader.js: -------------------------------------------------------------------------------- 1 | booktorious.reader = { 2 | initialize: function (epub) { 3 | booktorious.loadTemplate("reader"); 4 | var bookContent = sarge("#book_content"); 5 | for (var i = 0, il = epub.opf.spine.length; i < il; i++) { 6 | var spineEntry = booktorious 7 | .create(booktorious.spineEntry); 8 | spineEntry.initialize(epub, i); 9 | spineEntry.renderIframe(bookContent); 10 | } 11 | } 12 | }; -------------------------------------------------------------------------------- /vendor/sarge/README.textile: -------------------------------------------------------------------------------- 1 | h2. Sarge 2 | 3 | Cross browser JavaScript DOM library. 4 | 5 | Sarge itself does not implement any functionality for DOM interaction, this is provided via plugins. 6 | 7 | That's great becaue you can choose how hacky the implementation of the DOM interaction is. Writing a HTML5 app and don't care about IE? Great, use a lightweight hack-free plugin. Need to support IE? Use a hackish IE plugin. 8 | 9 | In other words, you get to use the same top level API - sarge itself - and change the implementation of the DOM interaction via plugins. 10 | 11 | Work in progress. -------------------------------------------------------------------------------- /public/js/booktorious.js: -------------------------------------------------------------------------------- 1 | this.booktorious = { 2 | loadTemplate: function (name, data) { 3 | var element = sarge("#templates script[data-template-name=" + name + "]"); 4 | var template = new Copperplate(element.html()); 5 | 6 | if (!this.hasOwnProperty("wrapper")) { 7 | this.wrapper = sarge("#wrapper"); 8 | } 9 | 10 | this.wrapper.html(template.html(data || {})); 11 | }, 12 | 13 | create: (function () { 14 | function F(){}; 15 | return function (proto) { 16 | F.prototype = proto; 17 | return new F(); 18 | }; 19 | }()) 20 | }; -------------------------------------------------------------------------------- /public/screen.css: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: #333; 3 | } 4 | 5 | body { 6 | font-family: Arial, sans-serif; 7 | font-size: 14px; 8 | margin: 0; 9 | } 10 | 11 | .section { 12 | width: 960px; 13 | margin: 20px auto; 14 | } 15 | 16 | #wrapper { 17 | padding: 10px; 18 | background-color: #fff; 19 | } 20 | 21 | #footer { 22 | color: #fff; 23 | } 24 | 25 | #footer a { 26 | color: #999; 27 | } 28 | 29 | h1 { 30 | color: #fff; 31 | background-color: #369; 32 | font-size: 40px; 33 | margin: 0; 34 | padding: 10px; 35 | } 36 | 37 | h2 { 38 | font-size: 25px; 39 | border-bottom: 1px solid #ccc; 40 | } 41 | 42 | .error { 43 | background: #fdd; 44 | padding: 10px; 45 | font-size: 16px; 46 | } 47 | 48 | #book_content iframe { 49 | border: 0; 50 | overflow: hidden; 51 | display: block; 52 | width: 100%; 53 | background-color: #fff; 54 | margin-bottom: 20px; 55 | } -------------------------------------------------------------------------------- /vendor/sarge-eventually/sarge-eventually.js: -------------------------------------------------------------------------------- 1 | sarge.api.bindEvent = function(element, event, handler) { 2 | var wrappedHandler = function (e) { 3 | if (handler.call(this, e) === false) { 4 | if (e.stopPropagation && e.preventDefault) { 5 | e.stopPropagation(); 6 | e.preventDefault(); 7 | } else { // Assuming IE 8 | e.cancelBubble = true; 9 | e.returnValue = false; 10 | } 11 | } 12 | } 13 | 14 | if (element.attachEvent) { 15 | element.attachEvent("on" + event, wrappedHandler); 16 | } else if (element.addEventListener) { 17 | element.addEventListener(event, wrappedHandler, false); 18 | } 19 | }; 20 | 21 | sarge.api.triggerEvent = function(element, eventName) { 22 | if (document.createEvent) { 23 | var event = document.createEvent("HTMLEvents"); 24 | 25 | // Type, can bubble, cancelable. 26 | event.initEvent(eventName, true, true); 27 | 28 | element.dispatchEvent(event); 29 | } else if (element.fireEvent) { 30 | element.fireEvent("on" + eventName); 31 | } 32 | }; -------------------------------------------------------------------------------- /vendor/sarge-eventually/test/tests/sarge-eventually_test.js: -------------------------------------------------------------------------------- 1 | TestCase("SargeEventuallyTest", { 2 | setUp: function () { 3 | /*:DOC element =
*/ 4 | this.e = sarge(this.element); 5 | }, 6 | 7 | "test binding and triggering event": function () { 8 | var timesCalled = 0; 9 | 10 | this.e.event("click", function () { 11 | timesCalled++; 12 | }); 13 | 14 | this.e.event("click"); 15 | this.e.event("click"); 16 | 17 | assertEquals(2, timesCalled); 18 | }, 19 | 20 | 21 | "test returning false to stop propagation and default": function () { 22 | /*:DOC+=
*/ 23 | 24 | var calls = []; 25 | var foo = sarge(document.getElementById("foo")); 26 | var bar = sarge(document.getElementById("bar")); 27 | 28 | foo.event("click", function () { 29 | calls.push("foo"); 30 | return false; 31 | }); 32 | 33 | bar.event("click", function () { 34 | calls.push("bar"); 35 | return false; 36 | }); 37 | 38 | bar.event("click"); 39 | 40 | assertEquals(["bar"], calls); 41 | } 42 | }); -------------------------------------------------------------------------------- /public/js/booktorious/loading.js: -------------------------------------------------------------------------------- 1 | booktorious.loading = { 2 | initialize: function (blob) { 3 | var self = this; 4 | booktorious.loadTemplate("loading"); 5 | this.blob = blob; 6 | this.messageTarget = sarge("#message"); 7 | this.epub = new JSEpub(this.blob); 8 | this.epub.processInSteps(function (step, extras) { 9 | self.step(step, extras); 10 | }); 11 | }, 12 | 13 | step: function (stepId, extras) { 14 | if (stepId === 1) { 15 | this.addMessage("Unarchiving"); 16 | } else if (stepId === 2) { 17 | this.addMessage("Uncompressing " + extras); 18 | } else if (stepId === 3 || stepId === 4) { 19 | this.addMessage("Post processing") 20 | } else if (stepId === 5) { 21 | booktorious 22 | .create(booktorious.reader) 23 | .initialize(this.epub); 24 | } else if (stepId === -1) { 25 | this.addMessage("The file does not seem to be an EPUB."); 26 | } else if (stepId < 0) { 27 | this.addMessage("An unknown error occured."); 28 | } else { 29 | // Wtf. 30 | } 31 | }, 32 | 33 | addMessage: function (msg) { 34 | this.messageTarget.html(msg); 35 | } 36 | }; -------------------------------------------------------------------------------- /public/js/booktorious/spine_entry.js: -------------------------------------------------------------------------------- 1 | booktorious.spineEntry = { 2 | initialize: function (epub, i) { 3 | this.epub = epub; 4 | this.href = this.epub.opf.manifest[this.epub.opf.spine[i]]["href"]; 5 | this.doc = this.epub.files[this.href]; 6 | this.iframe = document.createElement("iframe"); 7 | }, 8 | 9 | renderIframe: function (target) { 10 | var self = this; 11 | 12 | target.append(this.iframe); 13 | 14 | this.iframe.contentDocument.open(); 15 | var html = new XMLSerializer().serializeToString(this.doc); 16 | this.iframe.contentDocument.write(html); 17 | this.contentResizeIframe(); 18 | this.iframe.contentDocument.close(); 19 | 20 | sarge(this.iframe.contentDocument).find("img").event("load", function () { 21 | self.contentResizeIframe(); 22 | }); 23 | 24 | var links = sarge(this.iframe.contentDocument).find("a"); 25 | links.event("mouseover", function (e) {}); 26 | links.event("mouseout", function (e) {}); 27 | links.event("click", function (e) { 28 | // self.didClickAnchor(sarge(this).attr("href")); 29 | // alert("Internal linking doesn't work yet."); 30 | return false; 31 | }); 32 | }, 33 | 34 | contentResizeIframe: function () { 35 | sarge(this.iframe) 36 | .attr("height", this.iframe.contentDocument.height) 37 | .attr("height", this.iframe.contentDocument.documentElement.scrollHeight); 38 | } 39 | }; -------------------------------------------------------------------------------- /vendor/copperplate/README.textile: -------------------------------------------------------------------------------- 1 | h2. Copperplate. 2 | 3 | JavaScript templating engine with HTML syntax. 4 | 5 | HTML syntax is a good idea. It blends in with your text editors HTML mode. Proper indentation in if/else blocks with no editor configuration, yay! 6 | 7 | h2. Control flow 8 | 9 | If and else. 10 | 11 |
var t = new Copperplate("Hello, World!Moon.");
12 | 
13 | t.html({thing: true}); // "Hello, World!"
14 | t.html({thing: false}); // "Hello, Moon."
15 | 16 | Elseif. 17 | 18 |
var t = new Copperplate("OneTwo");
19 | 
20 | t.html({one: true, two: false}); // "One"
21 | t.html({one: false, two: true}); // "Two"
22 | t.html({one: false, two: false}); // ""
23 | 24 | Nesting. 25 | 26 |
var t = new Copperplate(""
27 |                + ""
28 |                +   "two"
29 |                +   ""
30 |                +     "three"
31 |                +   ""
32 |                + "");
33 | 
34 | t.html({one: true, two: true, three: false}); // "two"
35 | t.html({one: true, two: false, three: false}); // ""
36 | t.html({one: true, two: false, three: true}); // "three"
37 | t.html({one: false, two: false, three: true}); // ""
38 | 
39 | 40 | 41 | h2. Variables 42 | 43 | The variable syntax is {{foo}}. 44 | 45 |
 var t = new Copperplate('Hello, {{thing}}!');
46 | t.html({thing: "World"}) // "Hello, World!";
47 | t.html({thing: "Moon"})  // "Hello, Moon!";
-------------------------------------------------------------------------------- /public/js/application.js: -------------------------------------------------------------------------------- 1 | sarge(window).event("load", function () { 2 | var fileReaderSupported = ("FileReader" in window); 3 | 4 | function showLoading(blob) { 5 | booktorious.create(booktorious.loading).initialize(blob); 6 | }; 7 | 8 | function handleFiles(files) { 9 | var file = files[0]; 10 | if (file) { 11 | var reader = new FileReader(); 12 | reader.onloadend = function () { 13 | showLoading(reader.result); 14 | }; 15 | reader.onerror = function (event) { 16 | alert("An error occurred while reading the file. Error code: " + event.target.error.code); 17 | }; 18 | reader.readAsBinaryString(file); 19 | } else { 20 | alert("No file chosen."); 21 | } 22 | }; 23 | 24 | booktorious.loadTemplate("choose_file", { 25 | inputFileReadSupported: fileReaderSupported 26 | }); 27 | 28 | if (!fileReaderSupported) { return; } 29 | 30 | var fileInput = sarge("input[type=file]").element(); 31 | sarge("button").event("click", function () { 32 | if ("files" in fileInput) { 33 | handleFiles(fileInput.files); 34 | } 35 | }); 36 | 37 | sarge(window) 38 | .event("dragover", function () { 39 | return false; 40 | }) 41 | .event("drop", function (e) { 42 | if ("dataTransfer" in e) { 43 | if ("files" in e.dataTransfer) { 44 | handleFiles(e.dataTransfer.files); 45 | } 46 | } 47 | return false; 48 | }); 49 | }); -------------------------------------------------------------------------------- /public/js/sarge-fu.js: -------------------------------------------------------------------------------- 1 | /* A custom Firefox/Chrome compatible API implementation for sarge. */ 2 | sarge.api.getAttribute = function (element, attribute) { 3 | return element.getAttribute(attribute); 4 | }; 5 | 6 | sarge.api.setAttribute = function (element, attribute, value) { 7 | element.setAttribute(attribute, value); 8 | }; 9 | 10 | sarge.api.getHtml = function (element) { 11 | return element.innerHTML; 12 | }; 13 | 14 | sarge.api.setHtml = function (element, html) { 15 | if (html instanceof sarge) { 16 | sarge(element).clear().append(html); 17 | } else { 18 | element.innerHTML = html; 19 | } 20 | }; 21 | 22 | 23 | sarge.api.clear = function (element) { 24 | while (element.firstChild) { 25 | element.removeChild(element.firstChild); 26 | } 27 | }; 28 | 29 | sarge.api.append = function (element, html) { 30 | if (html instanceof sarge) { 31 | element.appendChild(html.element()); 32 | } else if (typeof html === "string") { 33 | element.innerHTML = element.innerHTML + html; 34 | } else { 35 | element.appendChild(html); 36 | } 37 | }; 38 | 39 | 40 | sarge.api.hide = function (element) { 41 | element.style.display = "none"; 42 | }; 43 | 44 | sarge.api.show = function (element) { 45 | element.style.display = "inherit"; 46 | }; 47 | 48 | 49 | (function () { 50 | var regexpSpecials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); 51 | var regexpEscape = function (string) { 52 | return string.replace(regexpSpecials, "\\$&"); 53 | }; 54 | var classRegexp = function (cssClass) { 55 | return new RegExp("\\s?\\b" + regexpEscape(cssClass) + "\\b\\s?"); 56 | }; 57 | 58 | sarge.api.addClass = function (element, cssClass) { 59 | var s = sarge(element); 60 | if (!s.hasClass(cssClass)) { 61 | var current = s.attr("class"); 62 | if (current) { 63 | s.attr("class", current + " " + cssClass); 64 | } else { 65 | s.attr("class", cssClass); 66 | } 67 | } 68 | }; 69 | 70 | sarge.api.removeClass = function (element, cssClass) { 71 | var s = sarge(element); 72 | var r = classRegexp(cssClass); 73 | if (s.hasClass(cssClass)) { 74 | s.attr("class", s.attr("class").replace(r, "")); 75 | } 76 | }; 77 | 78 | sarge.api.hasClass = function (element, cssClass) { 79 | var r = classRegexp(cssClass); 80 | return r.test(sarge(element).attr("class")); 81 | }; 82 | }()); -------------------------------------------------------------------------------- /vendor/sarge/doc/api.txt: -------------------------------------------------------------------------------- 1 | Sarge 1.0 plugin API documentation 2 | ================================== 3 | 4 | Documentation for the API that Sarge plugins use to implement functionality in Sarge. 5 | 6 | :Author: August Lilleaas 7 | :Email: 8 | 9 | :All elements: The function is called on all the elements in the sarge instance. 10 | :One element: The function is only called on the first element in the sarge instance. 11 | 12 | Sarge Core 13 | ---------- 14 | 15 | cssSelect(selector) 16 | ~~~~~~~~~~~~~~~~~~~ 17 | 18 | Performs a CSS selector query on `document`. Returns an array. 19 | 20 | [source,javascript] 21 | sarge("a css selector"); 22 | 23 | cssSelect(selector,rootNode) 24 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 25 | 26 | Like `cssSelect`, but runs the query on `rootNode` instead of `document`. 27 | 28 | [source,javascript] 29 | sarge(foo).find("a css selector"); 30 | 31 | 32 | Attributes 33 | ---------- 34 | 35 | attr(name) 36 | ~~~~~~~~~~ 37 | 38 | Gets an attribute. 39 | 40 | [source,javascript] 41 | sarge(foo).attr("id"); 42 | 43 | {oneelement} 44 | 45 | attr(name,value) 46 | ~~~~~~~~~~~~~~~~ 47 | 48 | Sets an attribute. 49 | 50 | [source,javascript] 51 | sarge(foo).attr("href", myHref); 52 | 53 | {allelements} 54 | 55 | addClass(class) 56 | ~~~~~~~~~~~~~~~ 57 | 58 | Adds a class to the elements. 59 | 60 | [source,javascript] 61 | sarge(foo).addClass("my_class"); 62 | 63 | {allelements} 64 | 65 | removeClass(class) 66 | ~~~~~~~~~~~~~~~~~~ 67 | 68 | Removes a class to the elements. 69 | 70 | [source,javascript] 71 | sarge(foo).removeClass("my_class"); 72 | 73 | {allelements} 74 | 75 | hasClass(class) 76 | ~~~~~~~~~~~~~~~ 77 | 78 | Checks if the element has a class. 79 | 80 | [source,javascript] 81 | sarge(foo).hasClass("my_class"); 82 | 83 | {oneelement} 84 | 85 | CSS 86 | --- 87 | 88 | hide() 89 | ~~~~~~ 90 | 91 | Hides the elements. 92 | 93 | [source,javascript] 94 | sarge(foo).hide(); 95 | 96 | {allelements} 97 | 98 | show() 99 | ~~~~~~ 100 | 101 | Shows the elements. 102 | 103 | [source,javascript] 104 | sarge(foo).show(); 105 | 106 | {allelements} 107 | 108 | Events 109 | ------ 110 | 111 | event(name) 112 | ~~~~~~~~~~~ 113 | 114 | Triggers an event. 115 | 116 | [source,javascript] 117 | sarge(foo).event("click"); 118 | 119 | {allelements} 120 | 121 | event(name, function) 122 | ~~~~~~~~~~~~~~~~~~~~~ 123 | 124 | Binds an event. 125 | 126 | [source,javascript] 127 | sarge(foo).event("click", function () { ... }); 128 | 129 | {allelements} 130 | 131 | 132 | Manipulation 133 | ------------ 134 | 135 | append(thing) 136 | ~~~~~~~~~~~~~ 137 | 138 | Appends HTML to an element. 139 | 140 | [source,javascript] 141 | sarge(foo).append(thing); 142 | 143 | {allelements} 144 | 145 | clear() 146 | ~~~~~~~ 147 | 148 | Removes all content from the elements. 149 | 150 | [source,javascript] 151 | sarge(foo).clear(); 152 | 153 | {allelements} 154 | 155 | html() 156 | ~~~~~~ 157 | 158 | Gets the inner HTML. 159 | 160 | [source,javascript] 161 | sarge(foo).html(); 162 | 163 | {oneelement} 164 | 165 | html(myHtml) 166 | ~~~~~~~~~~~~ 167 | 168 | Sets the inner HTML of an element. 169 | [source,javascript] 170 | sarge(foo).html(myHtml); 171 | 172 | {allelements} 173 | 174 | Traversing 175 | ---------- 176 | 177 | find(selector) 178 | ~~~~~~~~~~~~~~ 179 | 180 | Finds children by CSS selector. Returns a new sarge instance. 181 | 182 | [source,javascript] 183 | sarge(foo).find(".my_thing"); 184 | 185 | {oneelement} 186 | -------------------------------------------------------------------------------- /vendor/sarge/sarge.js: -------------------------------------------------------------------------------- 1 | (function (GLOBAL) { 2 | /* 3 | * The core. Does nothing useful by itself. Needs plugins. 4 | * 5 | */ 6 | var sarge = function (obj) { 7 | if (!(this instanceof sarge)) { 8 | return new sarge(obj); 9 | } 10 | 11 | var t = typeof(obj); 12 | 13 | if (t === "string") { 14 | this.elements = this.callApiFunction("cssSelect", obj); 15 | } else if (obj instanceof sarge) { 16 | return obj; 17 | } else if (obj instanceof Array) { 18 | this.elements = obj; 19 | } else if (t === "object") { 20 | this.elements = [obj]; 21 | } else { 22 | throw new TypeError("Invalid object '" + obj + "' passed to sarge()."); 23 | } 24 | }; 25 | 26 | // API functions lives here. 27 | sarge.api = {}; 28 | 29 | sarge.prototype = { 30 | event: function (name, listener) { 31 | if (listener) { 32 | this.withElements("bindEvent", name, listener); 33 | } else { 34 | this.withElements("triggerEvent", name); 35 | } 36 | 37 | return this; 38 | }, 39 | 40 | html: function (html) { 41 | if (html === undefined) { 42 | return this.withElement("getHtml"); 43 | } else { 44 | this.withElements("setHtml", html); 45 | return this; 46 | } 47 | }, 48 | 49 | append: function (html) { 50 | this.withElements("append", html); 51 | return this; 52 | }, 53 | 54 | clear: function () { 55 | this.withElements("clear"); 56 | return this; 57 | }, 58 | 59 | find: function (selector) { 60 | return sarge(this.callApiFunction("cssSelect", selector, this.element())); 61 | }, 62 | 63 | hide: function () { 64 | this.withElements("hide"); 65 | return this; 66 | }, 67 | 68 | show: function () { 69 | this.withElements("show"); 70 | return this; 71 | }, 72 | 73 | addClass: function (className) { 74 | this.withElements("addClass", className); 75 | return this; 76 | }, 77 | 78 | removeClass: function (className) { 79 | this.withElements("removeClass", className); 80 | return this; 81 | }, 82 | 83 | hasClass: function (className) { 84 | return this.withElement("hasClass", className); 85 | }, 86 | 87 | attr: function (attribute, value) { 88 | if (value === undefined) { 89 | return this.withElement("getAttribute", attribute); 90 | } else { 91 | this.withElements("setAttribute", attribute, value); 92 | return this; 93 | } 94 | }, 95 | 96 | /* 97 | * Returns the first element in the list, or throws an error 98 | * if no elements are in the list. 99 | */ 100 | element: function () { 101 | if (this.elements.length === 0) { 102 | throw "No elements." 103 | } else { 104 | return this.elements[0]; 105 | } 106 | }, 107 | 108 | withElement: function () { 109 | return this.callApiFunctionForElement(this.element(), arguments); 110 | }, 111 | 112 | /* 113 | * Calls API function once for each element in this.elements. 114 | * 115 | * First argument is the name of the API function. The API function 116 | * will get [theElement, arguments, passed, to, withElements, here] 117 | * passed to it. 118 | * 119 | */ 120 | withElements: function () { 121 | for (var i = 0, il = this.elements.length; i < il; i++) { 122 | this.callApiFunctionForElement(this.elements[i], arguments); 123 | } 124 | }, 125 | 126 | callApiFunctionForElement: function (element, args) { 127 | var args = Array.prototype.slice.call(args); 128 | // Add element as 2nd item in array. 129 | args.splice(1, 0, element); 130 | return this.callApiFunction.apply(this, args); 131 | }, 132 | 133 | /* 134 | * Calls an API function. An API function is a function on 135 | * sarge.Instance.prototype. 136 | * 137 | * The first argument is the name of the API function. All successive 138 | * arguments are passed to that function. 139 | * 140 | */ 141 | callApiFunction: function () { 142 | var args = Array.prototype.slice.call(arguments); 143 | var apiFunc = args.shift(); 144 | 145 | if (typeof(sarge.api[apiFunc]) === "function") { 146 | return sarge.api[apiFunc].apply(this, args); 147 | } else { 148 | throw "Sarge: API for '" + apiFunc + "' has not been loaded."; 149 | } 150 | } 151 | }; 152 | 153 | GLOBAL.sarge = sarge; 154 | sarge.originalDollar = GLOBAL.$; 155 | GLOBAL.$ = sarge; 156 | 157 | sarge.noConflict = function () { 158 | GLOBAL.$ = sarge.originalDollar; 159 | } 160 | 161 | }(this)); -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | 23 |

Loading..

24 |

If you see this message for more than a second or so, your browser isn't supported.

25 |

The site has only been tested to work in Chromium 5 and Firefox 3.6.

26 |
27 |
28 | 29 | 33 | 34 |
35 | 93 | 97 | 100 |
101 | 102 | 103 | -------------------------------------------------------------------------------- /vendor/copperplate/copperplate.js: -------------------------------------------------------------------------------- 1 | (function (GLOBAL) { 2 | var Copperplate = function (template) { 3 | this.template = template; 4 | } 5 | 6 | Copperplate.prototype = { 7 | html: function (data) { 8 | var elements = this.template.match(/\<[^\>]+\>|[^\<]+/g); 9 | var out = []; 10 | var ifBlocks = []; 11 | var previousTagWasConditionalEndTag; 12 | 13 | for (var i = 0, il = elements.length; i < il; i++) { 14 | var e = this.insertVariables(elements[i], data); 15 | var ifBlocksEntry = ifBlocks[ifBlocks.length - 1]; 16 | 17 | // End tag 18 | if (/^\<\//.test(e)) { 19 | var t = this.getTag(e); 20 | 21 | if (t[0] === "if") { 22 | previousTagWasConditionalEndTag = true; 23 | ifBlocksEntry.previousEndTag = "if"; 24 | } else if (t[0] === "else") { 25 | previousTagWasConditionalEndTag = true; 26 | if (ifBlocks.length === 0) { 27 | throw Error("Unexpected else, not in a tree.") 28 | } 29 | 30 | ifBlocksEntry.previousEndTag = "else"; 31 | 32 | ifBlocks.pop(); 33 | } else if (t[0] === "elseif") { 34 | if (previousTagWasConditionalEndTag) { 35 | ifBlocks.pop(); 36 | } 37 | 38 | previousTagWasConditionalEndTag = true; 39 | if (ifBlocks.length === 0) { 40 | throw Error("Unexpected elseif, not in a tree.") 41 | } 42 | 43 | ifBlocksEntry.previousEndTag = "elseif"; 44 | } else { 45 | if (ifBlocksEntry) { 46 | if (previousTagWasConditionalEndTag && ifBlocksEntry.previousEndTag) { 47 | ifBlocks.pop(); 48 | } 49 | } 50 | 51 | if (!this.ifBlocksContainsFalse(ifBlocks)) { 52 | out.push(e); 53 | } 54 | } 55 | 56 | 57 | // Start tag 58 | } else if (/^\]+)/); 154 | if (rawAttrs) { 155 | data = rawAttrs[1].split(/ +/g); 156 | var attrs = []; 157 | for (var i = 0, il = data.length; i < il; i++) { 158 | var split = data[i].split("="); 159 | var val = split[1] || null; 160 | 161 | if (val) { 162 | val = val.replace(/^['"](.*?)['"]$/, "$1"); 163 | } 164 | 165 | attrs.push([split[0], val]); 166 | } 167 | out.push(attrs); 168 | } else { 169 | out.push([]); 170 | } 171 | 172 | return out; 173 | }, 174 | 175 | insertVariables: function (string, data) { 176 | return string.replace(/{{([^}]+)}}/, function (all, key) { 177 | return data[key] || ""; 178 | }); 179 | } 180 | } 181 | 182 | GLOBAL.Copperplate = Copperplate; 183 | }(this)); -------------------------------------------------------------------------------- /vendor/sarge/test/tests/sarge_test.js: -------------------------------------------------------------------------------- 1 | TestCase("SargeTest", { 2 | tearDown: function() { 3 | sarge.api = {}; 4 | }, 5 | 6 | "test passing string calls cssSelect": function () { 7 | sarge.api.cssSelect = sinon.spy(); 8 | 9 | sarge("foo"); 10 | sarge("bar"); 11 | 12 | assertTrue(sarge.api.cssSelect.calledTwice); 13 | assertTrue(sarge.api.cssSelect.getCall(0).calledWithExactly("foo")) 14 | assertTrue(sarge.api.cssSelect.getCall(1).calledWithExactly("bar")) 15 | }, 16 | 17 | "test passing string stores in elements": function () { 18 | sarge.api.cssSelect = function () { return [1, 2] }; 19 | var s = sarge("foo"); 20 | assertEquals([1, 2], s.elements); 21 | }, 22 | 23 | "test passing object stores in elements": function () { 24 | var obj = {}; 25 | var s = sarge(obj); 26 | assertEquals([obj], s.elements); 27 | }, 28 | 29 | "test passing array stores it as elements directly": function () { 30 | var s = sarge([1, 2]); 31 | assertEquals([1, 2], s.elements); 32 | }, 33 | 34 | "test passing sarge objects returns the sarge object": function () { 35 | sarge.api.cssSelect = sinon.stub(); 36 | var s = sarge("foo"); 37 | assertSame(s, sarge(s)); 38 | }, 39 | 40 | "test withElement": function () { 41 | sarge.api.hide = sinon.stub(); 42 | var s = sarge(["abc", "123"]); 43 | s.withElement("hide"); 44 | 45 | assertTrue(sarge.api.hide.calledOnce); 46 | assertTrue(sarge.api.hide.getCall(0).calledWithExactly("abc")); 47 | }, 48 | 49 | "test with elements": function () { 50 | var s = sarge([]); 51 | s.elements = ["foo", "bar", "baz"]; 52 | sarge.api.goWild = sinon.spy(); 53 | s.withElements("goWild", 123, "abc"); 54 | 55 | assertTrue(sarge.api.goWild.calledThrice); 56 | assertTrue(sarge.api.goWild.getCall(0).calledWithExactly("foo",123,"abc")); 57 | assertTrue(sarge.api.goWild.getCall(1).calledWithExactly("bar",123,"abc")); 58 | assertTrue(sarge.api.goWild.getCall(2).calledWithExactly("baz",123,"abc")); 59 | }, 60 | 61 | "test calling none existing api function": function () { 62 | expectAsserts(1); 63 | 64 | var s = sarge({}); 65 | try { 66 | s.callApiFunction("doesNotExist"); 67 | } catch (e) { 68 | assertTrue(true); 69 | } 70 | }, 71 | 72 | "test find": function () { 73 | sarge.api.cssSelect = function () { return [1, 2] }; 74 | var s = sarge({}).find("stubbed"); 75 | assertEquals([1, 2], s.elements); 76 | }, 77 | 78 | "test element with elements": function () { 79 | var s = sarge(["abc", "123"]); 80 | assertEquals("abc", s.element()); 81 | }, 82 | 83 | "test element without elements": function () { 84 | expectAsserts(1); 85 | var s = sarge([]); 86 | 87 | try { 88 | s.element() 89 | } catch(e) { 90 | assertTrue(true); 91 | } 92 | }, 93 | 94 | "test dollar and noConflict": function () { 95 | // TODO: Figure out how to test this properly. 96 | } 97 | }); 98 | 99 | TestCase("SargeApiProxiesTest", { 100 | tearDown: function() { 101 | sarge.api = {}; 102 | }, 103 | 104 | "test binding event": function () { 105 | var obj = {}; 106 | var listener = function () {}; 107 | sarge.api.bindEvent = sinon.stub(); 108 | 109 | var s = sarge(obj); 110 | assertSame(s, s.event("foo", listener)); 111 | assertTrue(sarge.api.bindEvent.calledOnce); 112 | assertTrue(sarge.api.bindEvent.getCall(0).calledWithExactly(obj, "foo", listener)); 113 | }, 114 | 115 | "test triggering event": function () { 116 | var obj = {}; 117 | sarge.api.triggerEvent = sinon.stub(); 118 | 119 | var s = sarge(obj); 120 | assertSame(s, s.event("foo")); 121 | 122 | assertTrue(sarge.api.triggerEvent.calledOnce); 123 | assertTrue(sarge.api.triggerEvent.getCall(0).calledWithExactly(obj, "foo")); 124 | }, 125 | 126 | "test getting html": function () { 127 | var obj = {}; 128 | var args; 129 | sarge.api.getHtml = sinon.spy(function (el) { return "foo" }); 130 | 131 | var s = sarge(obj); 132 | 133 | assertEquals("foo", s.html()); 134 | assertTrue(sarge.api.getHtml.calledOnce); 135 | assertTrue(sarge.api.getHtml.getCall(0).calledWithExactly(obj)) 136 | }, 137 | 138 | "test setting html": function () { 139 | var obj = {}; 140 | sarge.api.setHtml = sinon.spy();; 141 | 142 | var s = sarge(obj); 143 | assertSame(s, s.html("foo")); 144 | assertTrue(sarge.api.setHtml.calledOnce); 145 | assertTrue(sarge.api.setHtml.getCall(0).calledWithExactly(obj, "foo")); 146 | }, 147 | 148 | "test append": function () { 149 | var obj = {}; 150 | sarge.api.append = sinon.spy(); 151 | 152 | var s = sarge(obj); 153 | assertSame(s, s.append("foo")); 154 | assertTrue(sarge.api.append.calledOnce); 155 | assertTrue(sarge.api.append.getCall(0).calledWithExactly(obj, "foo")) 156 | }, 157 | 158 | "test clear": function () { 159 | var obj = {}; 160 | sarge.api.clear = sinon.spy(); 161 | 162 | var s = sarge(obj); 163 | assertSame(s, s.clear()); 164 | assertTrue(sarge.api.clear.calledOnce); 165 | assertTrue(sarge.api.clear.getCall(0).calledWithExactly(obj)); 166 | }, 167 | 168 | "test find": function () { 169 | var obj = {}; 170 | var args; 171 | sarge.api.cssSelect = sinon.spy(function () { return [] }); 172 | 173 | var s = sarge(obj); 174 | var res = s.find("foo"); 175 | assertNotSame(s, res); 176 | assertTrue(res instanceof sarge); 177 | 178 | assertTrue(sarge.api.cssSelect.calledOnce) 179 | assertTrue(sarge.api.cssSelect.getCall(0).calledWithExactly("foo", obj)) 180 | }, 181 | 182 | "test hide": function () { 183 | var obj = {}; 184 | sarge.api.hide = sinon.spy(); 185 | 186 | var s = sarge(obj); 187 | assertSame(s, s.hide()); 188 | assertTrue(sarge.api.hide.calledOnce) 189 | assertTrue(sarge.api.hide.getCall(0).calledWithExactly(obj)); 190 | }, 191 | 192 | "test show": function () { 193 | var obj = {}; 194 | sarge.api.show = sinon.spy(); 195 | 196 | var s = sarge(obj); 197 | assertSame(s, s.show()); 198 | assertTrue(sarge.api.show.calledOnce); 199 | assertTrue(sarge.api.show.getCall(0).calledWithExactly(obj)); 200 | }, 201 | 202 | "test addClass": function () { 203 | var obj = {}; 204 | sarge.api.addClass = sinon.spy(); 205 | 206 | var s = sarge(obj); 207 | assertSame(s, s.addClass("foo")); 208 | assertTrue(sarge.api.addClass.calledOnce); 209 | assertTrue(sarge.api.addClass.getCall(0).calledWithExactly(obj, "foo")); 210 | }, 211 | 212 | "test removeClass": function () { 213 | var obj = {}; 214 | sarge.api.removeClass = sinon.spy(); 215 | 216 | var s = sarge(obj); 217 | assertSame(s, s.removeClass("foo")); 218 | assertTrue(sarge.api.removeClass.calledOnce); 219 | assertTrue(sarge.api.removeClass.getCall(0).calledWithExactly(obj, "foo")); 220 | }, 221 | 222 | "test hasClass": function () { 223 | var obj = {}; 224 | var args; 225 | sarge.api.hasClass = sinon.spy(function () { return true }); 226 | 227 | var s = sarge(obj); 228 | assertTrue(s.hasClass("foo")); 229 | assertTrue(sarge.api.hasClass.calledOnce); 230 | assertTrue(sarge.api.hasClass.getCall(0).calledWithExactly(obj, "foo")) 231 | }, 232 | 233 | "test getting attr": function () { 234 | var obj = {}; 235 | var args; 236 | sarge.api.getAttribute = sinon.spy(function () { return "foo" }); 237 | 238 | var s = sarge(obj); 239 | assertEquals("foo", s.attr("anything")); 240 | assertTrue(sarge.api.getAttribute.calledOnce); 241 | assertTrue(sarge.api.getAttribute.getCall(0).calledWithExactly(obj, "anything")) 242 | }, 243 | 244 | "test setting attr": function () { 245 | var obj = {}; 246 | sarge.api.setAttribute = sinon.spy(); 247 | 248 | var s = sarge(obj); 249 | assertSame(s, s.attr("foo", "bar")); 250 | assertTrue(sarge.api.setAttribute.calledOnce); 251 | assertTrue(sarge.api.setAttribute.getCall(0).calledWithExactly(obj, "foo", "bar")) 252 | } 253 | }); -------------------------------------------------------------------------------- /vendor/copperplate/test/tests/copperplate_test.js: -------------------------------------------------------------------------------- 1 | TestCase("CopperplateControlFlowTest", { 2 | "test basic if": function () { 3 | var t = 'this'; 4 | 5 | assertEquals("this", new Copperplate(t).html({someCondition: true})); 6 | assertEquals("", new Copperplate(t).html({someCondition: false})); 7 | }, 8 | 9 | "test basic if with falsey value": function () { 10 | var t = 'this'; 11 | 12 | assertEquals("this", new Copperplate(t).html({someCondition: true})); 13 | assertEquals("", new Copperplate(t).html({})); 14 | }, 15 | 16 | "test with function as condition": function () { 17 | var t = new Copperplate('a'); 18 | 19 | assertEquals("a", t.html({a: function () { return true }})); 20 | assertEquals("", t.html({a: function () { return false }})); 21 | assertEquals("", t.html({a: function () { }})); 22 | }, 23 | 24 | "test with function as condition it sets the scope": function () { 25 | var t = new Copperplate('a'); 26 | obj = { 27 | a: function () { 28 | return this.shouldRender; 29 | } 30 | } 31 | 32 | obj.shouldRender = true; 33 | assertEquals("a", t.html(obj)); 34 | 35 | obj.shouldRender = false; 36 | assertEquals("", t.html(obj)); 37 | }, 38 | 39 | "test two ifs": function () { 40 | var t = 'thisthat'; 41 | 42 | assertEquals("this", new Copperplate(t).html({a: true, b: false})); 43 | assertEquals("thisthat", new Copperplate(t).html({a: true, b: true})); 44 | assertEquals("that", new Copperplate(t).html({a: false, b: true})); 45 | assertEquals("", new Copperplate(t).html({a: false, b: false})); 46 | }, 47 | 48 | "test basic if/else": function () { 49 | var t = 'thisthat'; 50 | 51 | assertEquals("this", new Copperplate(t).html({someCondition: true})); 52 | assertEquals("that", new Copperplate(t).html({someCondition: false})); 53 | }, 54 | 55 | "test basic if/else with whitespace": function () { 56 | var t = 'this that'; 57 | 58 | assertEquals("this ", new Copperplate(t).html({someCondition: true})); 59 | assertEquals(" that", new Copperplate(t).html({someCondition: false})); 60 | }, 61 | 62 | "test basic if/elseif": function () { 63 | var t = 'thisthat'; 64 | 65 | assertEquals("this", new Copperplate(t).html({a: true, b: false})); 66 | assertEquals("this", new Copperplate(t).html({a: true, b: true})); 67 | assertEquals("that", new Copperplate(t).html({a: false, b: true})); 68 | assertEquals("", new Copperplate(t).html({a: false, b: false})); 69 | }, 70 | 71 | "test if/elseif/else": function() { 72 | var t = 'foobarbaz'; 73 | 74 | assertEquals("foo", new Copperplate(t).html({a: true, b: false})); 75 | assertEquals("bar", new Copperplate(t).html({a:false, b: true})); 76 | assertEquals("baz", new Copperplate(t).html({a: false, b: false})); 77 | }, 78 | 79 | "test nested ifs": function () { 80 | var t = 'helloworld'; 81 | assertEquals("world", new Copperplate(t).html({ 82 | something: true, 83 | last: true, 84 | other: false 85 | })); 86 | }, 87 | 88 | "test nesting hell": function() { 89 | var t = 90 | 'a' 91 | + 'b' 92 | + 'c' 93 | + 'd' 94 | + '' 95 | + 'xefg'; 96 | 97 | assertEquals("a", new Copperplate(t).html({ 98 | a: true, b: false, c: false, d:false, e: false, f: false 99 | })); 100 | 101 | assertEquals("bc", new Copperplate(t).html({ 102 | a: false, b: true, c: true, d:false, e: false, f: false 103 | })); 104 | 105 | assertEquals("bd", new Copperplate(t).html({ 106 | a: false, b: true, c: false, d:true, e: false, f: false 107 | })); 108 | 109 | assertEquals("b", new Copperplate(t).html({ 110 | a: false, b: true, c: false, d:false, e: false, f: false 111 | })); 112 | 113 | assertEquals("xg", new Copperplate(t).html({ 114 | a: false, b: false, c: false, d:false, e: false, f: false 115 | })); 116 | 117 | assertEquals("xg", new Copperplate(t).html({ 118 | a: false, b: false, c: false, d:false, e: false, f: false 119 | })); 120 | 121 | assertEquals("xeg", new Copperplate(t).html({ 122 | a: false, b: false, c: false, d:false, e: true, f: false 123 | })); 124 | 125 | assertEquals("xef", new Copperplate(t).html({ 126 | a: false, b: false, c: false, d:false, e: true, f: true 127 | })); 128 | 129 | assertEquals("xf", new Copperplate(t).html({ 130 | a: false, b: false, c: false, d:false, e: false, f: true 131 | })); 132 | }, 133 | 134 | "test if with other content": function () { 135 | var t = '

foo a

b'; 136 | 137 | assertEquals("

foo a

", new Copperplate(t).html({ 138 | a: true, b: false 139 | })); 140 | 141 | assertEquals("

foo a

b", new Copperplate(t).html({ 142 | a: true, b: true 143 | })); 144 | 145 | assertEquals("

foo

b", new Copperplate(t).html({ 146 | a: false, b: true 147 | })); 148 | 149 | assertEquals("

foo

", new Copperplate(t).html({ 150 | a: false, b: false 151 | })); 152 | 153 | var t = 'a

hi

'; 154 | 155 | assertEquals('a

hi

', new Copperplate(t).html({a: true})); 156 | assertEquals('

hi

', new Copperplate(t).html({a: false})); 157 | }, 158 | 159 | "test if and else with other content": function () { 160 | var t = '

hi

ab

hi

'; 161 | 162 | assertEquals('

hi

a

hi

', new Copperplate(t).html({a: true})); 163 | assertEquals('

hi

b

hi

', new Copperplate(t).html({a: false})); 164 | 165 | var t = '

hi

ab hi'; 166 | assertEquals('

hi

a hi', new Copperplate(t).html({a: true})); 167 | assertEquals('

hi

b hi', new Copperplate(t).html({a: false})); 168 | 169 | var t = '

hi

ab hi'; 170 | assertEquals('

hi

b hi', new Copperplate(t).html({a: false})); 171 | }, 172 | 173 | "test if, else and elseif with other content": function () { 174 | var t = '

hi

aarf a'; 175 | 176 | assertEquals("

hi

a a", new Copperplate(t).html({ 177 | a: true, b: false 178 | })); 179 | 180 | assertEquals("

hi

arf a", new Copperplate(t).html({ 181 | a: false, b: true 182 | })); 183 | 184 | assertEquals("

hi

a", new Copperplate(t).html({ 185 | a: false, b: false 186 | })); 187 | 188 | var t = '

hi

aarf

hi

'; 189 | 190 | assertEquals("

hi

a

hi

", new Copperplate(t).html({ 191 | a: true, b: false 192 | })); 193 | 194 | assertEquals("

hi

arf

hi

", new Copperplate(t).html({ 195 | a: false, b: true 196 | })); 197 | 198 | assertEquals("

hi

hi

", new Copperplate(t).html({ 199 | a: false, b: false 200 | })); 201 | } 202 | }); 203 | 204 | TestCase("CopperplateVariablesTest", { 205 | "test variable": function () { 206 | var t = new Copperplate('Hello, {{thing}}!'); 207 | assertEquals("Hello, World!", t.html({thing: "World"})); 208 | assertEquals("Hello, Moon!", t.html({thing: "Moon"})); 209 | }, 210 | 211 | "test none existing variable": function () { 212 | var t = new Copperplate('Hello, {{thing}}!'); 213 | assertEquals("Hello, !", t.html({not: "here"})); 214 | }, 215 | 216 | "test false-y variable": function () { 217 | var t = new Copperplate('Hello, {{thing}}!'); 218 | assertEquals("Hello, !", t.html({thing: null})); 219 | assertEquals("Hello, !", t.html({thing: undefined})); 220 | assertEquals("Hello, !", t.html({thing: false})); 221 | }, 222 | 223 | "test variable in contidional": function () { 224 | var t = new Copperplate('{{b}}'); 225 | assertEquals("foo", t.html({a: true, b: "foo"})); 226 | assertEquals("", t.html({a: false, b: "foo"})); 227 | assertEquals("", t.html({a: true, b: ""})); 228 | }, 229 | 230 | "test variable in attribute": function () { 231 | var t = new Copperplate('{{title}}'); 232 | assertEquals('yo', t.html({href: "foo", title: "yo"})); 233 | } 234 | }); 235 | 236 | TestCase("CopperplateVariousTest", { 237 | "test getTag": function () { 238 | var gt = Copperplate.prototype.getTag; 239 | assertEquals("if", gt('')[0]); 240 | assertEquals([], gt('')[1]); 241 | 242 | assertEquals("if", gt('')[0]); 243 | assertEquals([["something", null]], gt('')[1]); 244 | 245 | assertEquals([["something","other"]], gt('')[1]); 246 | assertEquals("if", gt('')[0]); 247 | 248 | assertEquals("test", gt('')[0]); 249 | assertEquals([["test", null]], gt('')[1]); 250 | 251 | assertEquals([ 252 | ["something", null], 253 | ["other", "this"], 254 | ["baz", null], 255 | ["maz", null], 256 | ["foo", "bar"] 257 | ], gt('