├── .gitignore ├── .travis.yml ├── README.md ├── css └── qunit-1.10.0.css ├── ddl_builder ├── ddl_builder.js ├── handlebarsHelpers │ └── each_with_index.js ├── qunit │ ├── columnTypes.js │ ├── fixture.html │ ├── guessValueSeparators.js │ ├── headerNames.js │ ├── main.js │ └── recordCount.js └── templates │ ├── generic.sql │ ├── oracle.sql │ └── sqlite.sql ├── index.html ├── libs ├── date.format.js ├── handlebars-1.0.8.js ├── jquery.js ├── qunit-1.10.0.js └── require.js ├── main.js ├── nodetest.js ├── package.json ├── qunit.html ├── qunit_main.js ├── run-qunit.js └── text.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .project 3 | .settings 4 | /node_modules 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | script: 5 | - "node nodetest.js" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #DDL Builder 2 | 3 | [![Build Status](https://travis-ci.org/jakefeasel/DDLBuilder.png)](https://travis-ci.org/jakefeasel/DDLBuilder) 4 | 5 | ##About 6 | 7 | This code uses JavaScript to parse plain text, tabular data and transform it into valid DDL and DML scripts, usable for immediate insertion into a database. 8 | 9 | The plain text can be formatted in any number of different ways. The only assumptions are that the first row contains the header names, and that there is a consistent delimiter between each "cell". See ddl_builder/qunit/fixture.html for examples of the variety of formats supported. 10 | 11 | The code will determine the proper column type based on the data provided, including field length (based on the longest value). 12 | 13 | Right now, the database products supported are MySQL, Oracle, PostgreSQL, SQLite, and MS SQL Server (2008+). 14 | 15 | This was originally developed as a utility for [SQL Fiddle](http://sqlfiddle.com), to aid in the rapid construction of mock databases based on the text provided by people when they ask questions on sites like [StackOverflow](http://stackoverflow.com). 16 | 17 | ##Running the example 18 | 19 | I've provided an [extremely simple plain HTML UI](http://htmlpreview.github.com/?https://github.com/jakefeasel/DDLBuilder/blob/master/index.html) that makes uses of this library. If you download this project into any web-accessible folder and browse to /index.html, you will have the option to add input text and transform it into the expected DDL/DML. 20 | 21 | This should provide sufficient clarity as to how the whole thing works. If you are a bit lost, I suggest looking at /main.js; this contains the dependency loading, instantiation, UI bindings and execution calls that are used to make the library do useful things. You'll need to do something quite similar to make it work in your project. 22 | 23 | You can move around some of the paths of things fairly easily; you'll just need to make sure that wherever you move them, you update the paths in your main call to *requirejs.config()*. 24 | 25 | You can also run the QUnit tests by browsing to /qunit.html 26 | 27 | ##Technologies used 28 | 29 | - [RequireJS](http://requirejs.org) for dependency management and module loading 30 | - [jQuery](http://jquery.org) core, miscellaneous JS library 31 | - [Handlebars](http://handlebarsjs.org) JavaScript templates, used primarily for specifying output formats 32 | - [Steven Levithan's Date Format](http://blog.stevenlevithan.com/archives/date-time-format) for formatting dates (naturally) in the output 33 | - [QUnit](http://qunitjs.com) for Unit testing 34 | 35 | ##License 36 | 37 | Copyright Jake Feasel, 2012 38 | 39 | Released under the terms of the MIT License. -------------------------------------------------------------------------------- /css/qunit-1.10.0.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.10.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://qunitjs.com 5 | * 6 | * Copyright 2012 jQuery Foundation and other contributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | overflow: hidden; 71 | } 72 | 73 | #qunit-userAgent { 74 | padding: 0.5em 0 0.5em 2.5em; 75 | background-color: #2b81af; 76 | color: #fff; 77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 78 | } 79 | 80 | #qunit-modulefilter-container { 81 | float: right; 82 | } 83 | 84 | /** Tests: Pass/Fail */ 85 | 86 | #qunit-tests { 87 | list-style-position: inside; 88 | } 89 | 90 | #qunit-tests li { 91 | padding: 0.4em 0.5em 0.4em 2.5em; 92 | border-bottom: 1px solid #fff; 93 | list-style-position: inside; 94 | } 95 | 96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 97 | display: none; 98 | } 99 | 100 | #qunit-tests li strong { 101 | cursor: pointer; 102 | } 103 | 104 | #qunit-tests li a { 105 | padding: 0.5em; 106 | color: #c2ccd1; 107 | text-decoration: none; 108 | } 109 | #qunit-tests li a:hover, 110 | #qunit-tests li a:focus { 111 | color: #000; 112 | } 113 | 114 | #qunit-tests ol { 115 | margin-top: 0.5em; 116 | padding: 0.5em; 117 | 118 | background-color: #fff; 119 | 120 | border-radius: 5px; 121 | -moz-border-radius: 5px; 122 | -webkit-border-radius: 5px; 123 | } 124 | 125 | #qunit-tests table { 126 | border-collapse: collapse; 127 | margin-top: .2em; 128 | } 129 | 130 | #qunit-tests th { 131 | text-align: right; 132 | vertical-align: top; 133 | padding: 0 .5em 0 0; 134 | } 135 | 136 | #qunit-tests td { 137 | vertical-align: top; 138 | } 139 | 140 | #qunit-tests pre { 141 | margin: 0; 142 | white-space: pre-wrap; 143 | word-wrap: break-word; 144 | } 145 | 146 | #qunit-tests del { 147 | background-color: #e0f2be; 148 | color: #374e0c; 149 | text-decoration: none; 150 | } 151 | 152 | #qunit-tests ins { 153 | background-color: #ffcaca; 154 | color: #500; 155 | text-decoration: none; 156 | } 157 | 158 | /*** Test Counts */ 159 | 160 | #qunit-tests b.counts { color: black; } 161 | #qunit-tests b.passed { color: #5E740B; } 162 | #qunit-tests b.failed { color: #710909; } 163 | 164 | #qunit-tests li li { 165 | padding: 5px; 166 | background-color: #fff; 167 | border-bottom: none; 168 | list-style-position: inside; 169 | } 170 | 171 | /*** Passing Styles */ 172 | 173 | #qunit-tests li li.pass { 174 | color: #3c510c; 175 | background-color: #fff; 176 | border-left: 10px solid #C6E746; 177 | } 178 | 179 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 180 | #qunit-tests .pass .test-name { color: #366097; } 181 | 182 | #qunit-tests .pass .test-actual, 183 | #qunit-tests .pass .test-expected { color: #999999; } 184 | 185 | #qunit-banner.qunit-pass { background-color: #C6E746; } 186 | 187 | /*** Failing Styles */ 188 | 189 | #qunit-tests li li.fail { 190 | color: #710909; 191 | background-color: #fff; 192 | border-left: 10px solid #EE5757; 193 | white-space: pre; 194 | } 195 | 196 | #qunit-tests > li:last-child { 197 | border-radius: 0 0 5px 5px; 198 | -moz-border-radius: 0 0 5px 5px; 199 | -webkit-border-bottom-right-radius: 5px; 200 | -webkit-border-bottom-left-radius: 5px; 201 | } 202 | 203 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 204 | #qunit-tests .fail .test-name, 205 | #qunit-tests .fail .module-name { color: #000000; } 206 | 207 | #qunit-tests .fail .test-actual { color: #EE5757; } 208 | #qunit-tests .fail .test-expected { color: green; } 209 | 210 | #qunit-banner.qunit-fail { background-color: #EE5757; } 211 | 212 | 213 | /** Result */ 214 | 215 | #qunit-testresult { 216 | padding: 0.5em 0.5em 0.5em 2.5em; 217 | 218 | color: #2b81af; 219 | background-color: #D2E0E6; 220 | 221 | border-bottom: 1px solid white; 222 | } 223 | #qunit-testresult .module-name { 224 | font-weight: bold; 225 | } 226 | 227 | /** Fixture */ 228 | 229 | #qunit-fixture { 230 | position: absolute; 231 | top: -10000px; 232 | left: -10000px; 233 | width: 1000px; 234 | height: 1000px; 235 | } 236 | -------------------------------------------------------------------------------- /ddl_builder/ddl_builder.js: -------------------------------------------------------------------------------- 1 | /* 2 | * DDL Builder 3 | * Copyright Jake Feasel, 2012 4 | * Released under MIT license 5 | * For questions email admin at sqlfiddle dot com 6 | * http://github.com/jakefeasel/DDLBuilder 7 | */ 8 | 9 | define( 10 | [ 11 | "jQuery", 12 | "Handlebars", 13 | "DateFormat", 14 | 'text!./templates/generic.sql', 15 | 'text!./templates/oracle.sql', 16 | 'text!./templates/sqlite.sql', 17 | 'HandlebarsHelpers/each_with_index' 18 | ], 19 | function ($, Handlebars, dateFormat, generic_template, oracle_template, sqlite_template) { 20 | 21 | ddl_builder = function (args) { 22 | if (!args) args = {}; 23 | // output settings 24 | this.fieldPrefix = ''; 25 | this.fieldSuffix = ''; 26 | this.tablePrefix = ''; 27 | this.tableSuffix = ''; 28 | 29 | 30 | this.dateFormatMask = "yyyy-mm-dd HH:MM:ss"; 31 | 32 | this.charType = 'varchar'; 33 | this.intType = 'int'; 34 | this.floatType = 'numeric'; 35 | this.dateType = 'datetime'; 36 | 37 | // input settings 38 | this.valueSeparator = ''; 39 | 40 | this.column_count = 0; 41 | this.definition = { 42 | tableName: "Table1", 43 | columns: [/* sample column structure 44 | { 45 | name: 'id', 46 | type: 'int', 47 | length: '', 48 | db_type: 'int4' 49 | }, 50 | { 51 | name: 'name', 52 | type: 'char', 53 | length: 20, 54 | db_type: 'varchar(20)' 55 | } 56 | */], 57 | data: [/* sample data structure 58 | // r for "row", v for "value" 59 | {r:[{v:1},{v:'Jake'}]}, 60 | {r:[{v:2},{v:'Rachel'}]}, 61 | {r:[{v:3},{v:'Andrew'}]}, 62 | {r:[{v:4},{v:'Ada'}]}, 63 | {r:[{v:5},{v:'Lucy O\'Malley'}]} 64 | 65 | 66 | */] 67 | }; 68 | 69 | 70 | this.ddlTemplate = generic_template; 71 | 72 | this.compiledTemplate = Handlebars.compile(this.ddlTemplate); 73 | this.setup(args); 74 | return this; 75 | }; 76 | 77 | ddl_builder.prototype.setup = function (settings) { 78 | for (var opt in settings) 79 | { 80 | this[opt] = settings[opt]; 81 | } 82 | 83 | if (settings["ddlTemplate"]) 84 | this.compiledTemplate = Handlebars.compile(this.ddlTemplate); 85 | 86 | if (settings["tableName"]) 87 | this.definition.tableName = settings.tableName; 88 | 89 | return this; 90 | }; 91 | 92 | ddl_builder.prototype.setupForDBType = function (type,separator) { 93 | 94 | switch (type) 95 | { 96 | case 'SQL Server': 97 | this.setup({ 98 | statement_separator: separator, 99 | fieldPrefix: '[', 100 | fieldSuffix: ']', 101 | tablePrefix: '[', 102 | tableSuffix: ']' 103 | }); 104 | break; 105 | 106 | case 'MySQL': 107 | this.setup({ 108 | statement_separator: separator, 109 | fieldPrefix: '`', 110 | fieldSuffix: '`', 111 | tablePrefix: '`', 112 | tableSuffix: '`' 113 | }); 114 | break; 115 | case 'PostgreSQL': 116 | this.setup({ 117 | statement_separator: separator, 118 | dateType: 'timestamp', 119 | fieldPrefix: '"', 120 | fieldSuffix: '"' 121 | }); 122 | break; 123 | 124 | case 'Oracle': 125 | var template = oracle_template; 126 | 127 | this.setup({ 128 | dateFormatMask: 'dd-mmm-yyyy hh:MM:ss TT', 129 | statement_separator: separator, 130 | ddlTemplate: template, 131 | dateType: 'timestamp', 132 | charType: 'varchar2', 133 | fieldPrefix: '"', 134 | fieldSuffix: '"' 135 | }); 136 | break; 137 | 138 | 139 | 140 | case 'SQLite': 141 | var template = sqlite_template; 142 | 143 | 144 | this.setup({ 145 | fieldPrefix: '"', 146 | fieldSuffix: '"', 147 | tablePrefix: '"', 148 | tableSuffix: '"', 149 | statement_separator: separator, 150 | ddlTemplate: template, 151 | dateType: 'DATE', 152 | charType: 'TEXT', 153 | intType: 'INTEGER', 154 | floatType: 'REAL' 155 | }); 156 | break; 157 | 158 | 159 | } 160 | return this; 161 | }; 162 | 163 | ddl_builder.prototype.populateDBTypes = function () { 164 | for (var i=0;i this.definition.columns[j].length) 327 | { 328 | this.definition.columns[j].length = value.length; 329 | } 330 | 331 | tmpRow.push({v:value}); 332 | } 333 | 334 | } 335 | this.definition.data.push({r: tmpRow}); 336 | 337 | } 338 | 339 | } 340 | } 341 | this.populateDBTypes(); 342 | this.populateWrappers(); 343 | return this.render(); 344 | }; 345 | 346 | /* HandlebarsJS-using code below */ 347 | 348 | Handlebars.registerHelper("formatted_field", function(root) { 349 | 350 | var colType = ''; 351 | var index = -1; 352 | for (var j = 0; j < root.columns.length; j++) 353 | { 354 | if (root.columns[j]) 355 | index++; 356 | 357 | if (index == this.index) 358 | { 359 | colType = root.columns[j].type; 360 | break; 361 | } 362 | } 363 | 364 | 365 | if (!this.v.length || this.v.toUpperCase() == 'NULL') 366 | return 'NULL'; 367 | if (colType == 'charType') 368 | return new Handlebars.SafeString("'" + this.v + "'"); 369 | 370 | if (colType == 'dateType') { 371 | return new Handlebars.SafeString("'" + dateFormat("UTC:" + this.v, root.dateFormatMask) + "'"); 372 | } 373 | 374 | return this.v; 375 | }); 376 | 377 | Handlebars.registerHelper("column_name_for_index", function(root) { 378 | return root.columns[this.index].name; 379 | }); 380 | 381 | 382 | ddl_builder.prototype.render = function () { 383 | return this.compiledTemplate($.extend(this.definition, {"separator": this.statement_separator})); 384 | }; 385 | 386 | return ddl_builder; 387 | 388 | }); 389 | -------------------------------------------------------------------------------- /ddl_builder/handlebarsHelpers/each_with_index.js: -------------------------------------------------------------------------------- 1 | define(["Handlebars"], function (Handlebars) { 2 | 3 | Handlebars.registerHelper("each_with_index", function(array, fn) { 4 | var buffer = ""; 5 | var k=0; 6 | for (var i = 0, j = array.length; i < j; i++) { 7 | if (array[i]) 8 | { 9 | var item = array[i]; 10 | 11 | // stick an index property onto the item, starting with 0 12 | item.index = k; 13 | 14 | item.first = (k == 0); 15 | item.last = (k == array.length); 16 | 17 | // show the inside of the block 18 | buffer += fn.fn(item); 19 | 20 | k++; 21 | } 22 | } 23 | 24 | // return the finished buffer 25 | return buffer; 26 | 27 | }); 28 | 29 | // returns nothing 30 | }); -------------------------------------------------------------------------------- /ddl_builder/qunit/columnTypes.js: -------------------------------------------------------------------------------- 1 | define(["jQuery","QUnit", "DDLBuilder/ddl_builder"], function ($,QUnit,DDLBuilder) { 2 | 3 | return function (id,types) { 4 | var ddl_builder = new DDLBuilder({ddlTemplate: "{{#each_with_index columns}}{{#if index}},{{/if}}{{db_type}}{{/each_with_index}}}"}); 5 | QUnit.equal(ddl_builder.parse($("#" + id).html()), types, "Column types"); 6 | }; 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /ddl_builder/qunit/fixture.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | a,b 4 | 1,2 5 | 6 | 7 | Period Result 8 | 1 Green 9 | 1 Blue 10 | 1 Blue 11 | 1 Red 12 | 1 Blue 13 | 1 Blue 14 | 1 Blue 15 | 2 Green 16 | 2 Green 17 | 2 Green 18 | 3 Blue 19 | 2 Red 20 | 2 Red 21 | 22 | 23 | date_due date_paid amount_due amount_paid category_type 24 | 08/12/2012 08/12/2012 500 450 Income 25 | 08/13/2012 08/17/2012 200 300 Expense 26 | 09/15/2012 09/13/2012 300 300 Income 27 | 09/17/2012 09/16/2012 100 100 Income 28 | 29 | 30 | L | N 31 | ------------------- 32 | A | 1 33 | A | 3 34 | A | 5 35 | B | 5 36 | B | 7 37 | B | 9 38 | C | 1 39 | C | 2 40 | C | 3 41 | 42 | 43 | +-----------+--------------+----------+--------+ 44 | | IdPayment | Costs_IdCost | Date | Amount | 45 | +-----------+--------------+----------+--------+ 46 | | 1 | 2 |2012/09/10| 1000 | 47 | +-----------+--------------+----------+--------+ 48 | | 2 | 2 |2012/09/20| 3000 | 49 | +-----------+--------------+----------+--------+ 50 | | 3 | 2 |2012/10/01| 5000 | 51 | +-----------+--------------+----------+--------+ 52 | 53 | 54 | Cul 1 Cul 2 55 | ===================== 56 | A10000 Test 57 | A10001 Test 123 58 | A20000 Test 1 59 | A20001 Test 999 60 | A30000 Test 2 61 | A30002 Test 5555 62 | A40000 Test 3 63 | A40006 Test 84384848 64 | 65 | 66 | Scoreband| TotalNoOfPeople | AvgScore 67 | -------------------------------- 68 | -5 to 0 | 2 | -2 69 | 0 to 5 | 3 | 2 70 | 5 to 10 | 2 | 8 71 | 10 to 15 | 3 | 13.3 72 | 73 | 74 | c1 c2 c3 75 | 1A2 cat black 76 | 1G2 dog red 77 | B11 frog green 78 | 1G2 girl red 79 | 80 | 81 | customdata check service loc value 82 | 101 0 0 4 4 83 | 101 0 0 3 3 84 | 101 5 4 4 3 85 | 102 0 0 1 2 86 | 102 4 4 3 3 87 | 103 0 0 4 4 88 | 89 | 90 | 91 | Old New Time Entered Order Number 92 | NULL Step 1 4/30/12 10:43 1C2014A 93 | Step 1 Step 2 5/2/12 10:17 1C2014A 94 | Step 2 Step 3 5/2/12 10:28 1C2014A 95 | Step 3 Step 4 5/2/12 11:14 1C2014A 96 | Step 4 Step 5 5/2/12 11:19 1C2014A 97 | Step 5 Step 9 5/3/12 11:23 1C2014A 98 | NULL Step 1 5/18/12 15:49 1C2014B 99 | Step 1 Step 2 5/21/12 9:21 1C2014B 100 | Step 2 Step 3 5/21/12 9:34 1C2014B 101 | Step 3 Step 4 5/21/12 10:08 1C2014B 102 | Step 4 Step 5 5/21/12 10:09 1C2014B 103 | Step 5 Step 6 5/21/12 16:27 1C2014B 104 | Step 6 Step 9 5/21/12 18:07 1C2014B 105 | NULL Step 1 6/12/12 10:28 1C2014C 106 | Step 1 Step 2 6/13/12 8:36 1C2014C 107 | Step 2 Step 3 6/13/12 9:05 1C2014C 108 | Step 3 Step 4 6/13/12 10:28 1C2014C 109 | Step 4 Step 6 6/13/12 10:50 1C2014C 110 | Step 6 Step 8 6/13/12 12:14 1C2014C 111 | Step 8 Step 4 6/13/12 15:13 1C2014C 112 | Step 4 Step 5 6/13/12 15:23 1C2014C 113 | Step 5 Step 8 6/13/12 15:30 1C2014C 114 | Step 8 Step 9 6/18/12 14:04 1C2014C 115 | 116 | 117 | 118 | input|output 119 | 2012-03-12|2012-03-12 00:00:00 120 | 01/02/2003|2003-01-02 00:00:00 121 | 05-Mar-1995|1995-03-05 00:00:00 122 | April 5, 2010|2010-04-05 00:00:00 123 | 4/30/12 10:43|2012-04-30 10:43:00 124 | 125 | 126 | 127 | CompId CommaList 128 | 2 '122','54','90' 129 | 130 | 54 '53','76' 131 | 34 '87' 132 | 22 '98' 133 | 134 | 135 |
136 | -------------------------------------------------------------------------------- /ddl_builder/qunit/guessValueSeparators.js: -------------------------------------------------------------------------------- 1 | define(["jQuery","QUnit", "DDLBuilder/ddl_builder"], function ($,QUnit,DDLBuilder) { 2 | 3 | return function(id,sep) { 4 | 5 | var ddl_builder = new DDLBuilder(), 6 | result = ddl_builder.guessValueSeparator($("#" + id).html()); 7 | 8 | if (result.separator) 9 | QUnit.equal(ddl_builder.guessValueSeparator($("#" + id).html()).separator.toString(), sep.toString(), "Guessing Value Separators"); 10 | else 11 | QUnit.ok(false, "Guessing value separators failed with message:" + result.message); 12 | }; 13 | 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /ddl_builder/qunit/headerNames.js: -------------------------------------------------------------------------------- 1 | define(["jQuery","QUnit", "DDLBuilder/ddl_builder"], function ($,QUnit,DDLBuilder) { 2 | 3 | return function (id,headers) { 4 | var ddl_builder = new DDLBuilder({ddlTemplate: "{{#each_with_index columns}}{{#if index}},{{/if}}{{name}}{{/each_with_index}}}"}); 5 | QUnit.equal(ddl_builder.parse($("#" + id).html()), headers, "Finding header names"); 6 | }; 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /ddl_builder/qunit/main.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jQuery", 3 | "QUnit", 4 | "text!./fixture.html", 5 | "./columnTypes", 6 | "./guessValueSeparators", 7 | "./headerNames", 8 | "./recordCount", 9 | "DDLBuilder/ddl_builder" 10 | ], 11 | 12 | function ($,QUnit,fixtureContent, 13 | columnTypes,guessValueSeparators, 14 | headerNames,recordCount, 15 | DDLBuilder) { 16 | 17 | $("#qunit-fixture").append(fixtureContent); 18 | 19 | $("#qunit-fixture #ddlInputText span").each(function () { 20 | var $this = $(this); 21 | QUnit.test("Parsing " + $this.attr('id'), function () { 22 | columnTypes($this.attr('id'), $this.attr('types')); 23 | guessValueSeparators($this.attr('id'), $this.attr('valueSeparator')); 24 | headerNames($this.attr('id'), $this.attr('headers')); 25 | recordCount($this.attr('id'), $this.attr('recordCount')); 26 | }); 27 | }); 28 | 29 | QUnit.test("Various date format parsing attempts", function () { 30 | var content = $("#qunit-fixture #dateParse").html(), 31 | ddl_builder = new DDLBuilder({ddlTemplate: "[{{#each_with_index data}}{{#if index}},{{/if}}[{{#each_with_index r}}{{#if index}}, {{/if}}\"{{formatted_field ../..}}\"{{/each_with_index}}]{{/each_with_index}}]"}), 32 | result = ddl_builder.parse(content), 33 | parsedResult=null,i=0; 34 | 35 | try { 36 | parsedResult = $.parseJSON(result); 37 | } catch (err){} 38 | 39 | if (parsedResult) { 40 | for (i in parsedResult) { 41 | QUnit.equal(parsedResult[i][0], parsedResult[i][1], "Input date matches output date"); 42 | } 43 | } 44 | else 45 | QUnit.ok(false, "Reading date data failed: Unable to parse result to JSON array ("+ result +")"); 46 | 47 | }); 48 | } 49 | ); -------------------------------------------------------------------------------- /ddl_builder/qunit/recordCount.js: -------------------------------------------------------------------------------- 1 | define(["jQuery","QUnit", "DDLBuilder/ddl_builder"], function ($,QUnit,DDLBuilder) { 2 | 3 | return function (id,count) { 4 | var ddl_builder = new DDLBuilder({ddlTemplate: "[{{#each_with_index data}}{{#if index}},{{/if}}}{{index}}{{/each_with_index}}}]"}); 5 | var result = ddl_builder.parse($("#" + id).html()), 6 | parsedResult = false; 7 | 8 | try { 9 | parsedResult = $.parseJSON(result); 10 | } catch (err){} 11 | 12 | if (parsedResult) 13 | QUnit.equal(parsedResult.length, count, "Getting Record Count"); 14 | else 15 | QUnit.ok(false, "Getting Record Count failed: Unable to parse result to JSON array ("+ result +")"); 16 | }; 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /ddl_builder/templates/generic.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE {{{tablePrefix}}}{{tableName}}{{{tableSuffix}}} 2 | ({{#each_with_index columns}}{{#if index}}, {{/if}}{{{../fieldPrefix}}}{{name}}{{{../fieldSuffix}}} {{db_type}}{{/each_with_index}}) 3 | {{separator}} 4 | 5 | INSERT INTO {{{tablePrefix}}}{{tableName}}{{{tableSuffix}}} 6 | ({{#each_with_index columns}}{{#if index}}, {{/if}}{{{../fieldPrefix}}}{{name}}{{{../fieldSuffix}}}{{/each_with_index}}) 7 | VALUES 8 | {{#each_with_index data}}{{#if index}}, 9 | {{/if}}({{#each_with_index r}}{{#if index}}, {{/if}}{{formatted_field ../..}}{{/each_with_index}}){{/each_with_index}} 10 | {{separator}} 11 | -------------------------------------------------------------------------------- /ddl_builder/templates/oracle.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE {{{tablePrefix}}}{{tableName}}{{{tableSuffix}}} 2 | ({{#each_with_index columns}}{{#if index}}, {{/if}}{{{../fieldPrefix}}}{{name}}{{{../fieldSuffix}}} {{db_type}}{{/each_with_index}}) 3 | {{separator}} 4 | 5 | INSERT ALL {{#each_with_index data}} 6 | INTO {{{../tablePrefix}}}{{../tableName}}{{{../tableSuffix}}} ({{#each_with_index r}}{{#if index}}, {{/if}}{{{../../fieldPrefix}}}{{column_name_for_index ../..}}{{{../../fieldSuffix}}}{{/each_with_index}}) 7 | VALUES ({{#each_with_index r}}{{#if index}}, {{/if}}{{formatted_field ../..}}{{/each_with_index}}){{/each_with_index}} 8 | SELECT * FROM dual 9 | {{separator}} -------------------------------------------------------------------------------- /ddl_builder/templates/sqlite.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE {{tablePrefix}}{{tableName}}{{tableSuffix}} 2 | ({{#each_with_index columns}}{{#if index}}, {{/if}}{{{../fieldPrefix}}}{{name}}{{{../fieldSuffix}}} {{db_type}}{{/each_with_index}}) 3 | {{separator}} 4 | 5 | {{#each_with_index data}} 6 | INSERT INTO {{tablePrefix}}{{../tableName}}{{tableSuffix}} 7 | ({{#each_with_index ../columns}}{{#if index}}, {{/if}}{{{../../fieldPrefix}}}{{name}}{{{../../fieldSuffix}}}{{/each_with_index}}) 8 | VALUES 9 | ({{#each_with_index r}}{{#if index}}, {{/if}}{{formatted_field ../..}}{{/each_with_index}}) 10 | {{../separator}} 11 | 12 | {{/each_with_index}} -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DDL Builder 5 | 6 | 21 | 22 | 23 |

DDL Builder

24 |
25 | Convert Regularly-Formatted Text to Database Table Insert Scripts 26 | 27 |
28 | 29 | 30 |
31 | 32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 | 46 | | 47 | 48 | 53 | 54 | | 55 | 56 |
57 | 58 |
59 | 60 |

Brought to you by SQL Fiddle.

61 | 62 | -------------------------------------------------------------------------------- /libs/date.format.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Date Format 1.2.3 3 | * (c) 2007-2009 Steven Levithan 4 | * MIT license 5 | * 6 | * Includes enhancements by Scott Trenda 7 | * and Kris Kowal 8 | * 9 | * Accepts a date, a mask, or a date and a mask. 10 | * Returns a formatted version of the given date. 11 | * The date defaults to the current date/time. 12 | * The mask defaults to dateFormat.masks.default. 13 | */ 14 | dateFormat = function () { 15 | var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, 16 | timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, 17 | timezoneClip = /[^-+\dA-Z]/g, 18 | pad = function (val, len) { 19 | val = String(val); 20 | len = len || 2; 21 | while (val.length < len) val = "0" + val; 22 | return val; 23 | }; 24 | 25 | // Regexes and supporting functions are cached through closure 26 | return function (date, mask, utc) { 27 | var dF = dateFormat; 28 | 29 | // You can't provide utc if you skip other args (use the "UTC:" mask prefix) 30 | if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { 31 | mask = date; 32 | date = undefined; 33 | } 34 | 35 | // Passing date through Date applies Date.parse, if necessary 36 | date = date ? new Date(date) : new Date; 37 | if (isNaN(date)) throw SyntaxError("invalid date"); 38 | 39 | mask = String(dF.masks[mask] || mask || dF.masks["default"]); 40 | 41 | // Allow setting the utc argument via the mask 42 | if (mask.slice(0, 4) == "UTC:") { 43 | mask = mask.slice(4); 44 | utc = true; 45 | } 46 | 47 | var _ = utc ? "getUTC" : "get", 48 | d = date[_ + "Date"](), 49 | D = date[_ + "Day"](), 50 | m = date[_ + "Month"](), 51 | y = date[_ + "FullYear"](), 52 | H = date[_ + "Hours"](), 53 | M = date[_ + "Minutes"](), 54 | s = date[_ + "Seconds"](), 55 | L = date[_ + "Milliseconds"](), 56 | o = utc ? 0 : date.getTimezoneOffset(), 57 | flags = { 58 | d: d, 59 | dd: pad(d), 60 | ddd: dF.i18n.dayNames[D], 61 | dddd: dF.i18n.dayNames[D + 7], 62 | m: m + 1, 63 | mm: pad(m + 1), 64 | mmm: dF.i18n.monthNames[m], 65 | mmmm: dF.i18n.monthNames[m + 12], 66 | yy: String(y).slice(2), 67 | yyyy: y, 68 | h: H % 12 || 12, 69 | hh: pad(H % 12 || 12), 70 | H: H, 71 | HH: pad(H), 72 | M: M, 73 | MM: pad(M), 74 | s: s, 75 | ss: pad(s), 76 | l: pad(L, 3), 77 | L: pad(L > 99 ? Math.round(L / 10) : L), 78 | t: H < 12 ? "a" : "p", 79 | tt: H < 12 ? "am" : "pm", 80 | T: H < 12 ? "A" : "P", 81 | TT: H < 12 ? "AM" : "PM", 82 | Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), 83 | o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), 84 | S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] 85 | }; 86 | 87 | return mask.replace(token, function ($0) { 88 | return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); 89 | }); 90 | }; 91 | }(); 92 | 93 | // Some common format strings 94 | dateFormat.masks = { 95 | "default": "ddd mmm dd yyyy HH:MM:ss", 96 | shortDate: "m/d/yy", 97 | mediumDate: "mmm d, yyyy", 98 | longDate: "mmmm d, yyyy", 99 | fullDate: "dddd, mmmm d, yyyy", 100 | shortTime: "h:MM TT", 101 | mediumTime: "h:MM:ss TT", 102 | longTime: "h:MM:ss TT Z", 103 | isoDate: "yyyy-mm-dd", 104 | isoTime: "HH:MM:ss", 105 | isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", 106 | isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" 107 | }; 108 | 109 | // Internationalization strings 110 | dateFormat.i18n = { 111 | dayNames: [ 112 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 113 | "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 114 | ], 115 | monthNames: [ 116 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 117 | "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" 118 | ] 119 | }; 120 | 121 | // For convenience... 122 | Date.prototype.format = function (mask, utc) { 123 | return dateFormat(this, mask, utc); 124 | }; 125 | 126 | -------------------------------------------------------------------------------- /libs/handlebars-1.0.8.js: -------------------------------------------------------------------------------- 1 | // lib/handlebars/base.js 2 | 3 | /*jshint eqnull:true*/ 4 | this.Handlebars = {}; 5 | 6 | (function(Handlebars) { 7 | 8 | Handlebars.VERSION = "1.0.rc.2"; 9 | 10 | Handlebars.helpers = {}; 11 | Handlebars.partials = {}; 12 | 13 | Handlebars.registerHelper = function(name, fn, inverse) { 14 | if(inverse) { fn.not = inverse; } 15 | this.helpers[name] = fn; 16 | }; 17 | 18 | Handlebars.registerPartial = function(name, str) { 19 | this.partials[name] = str; 20 | }; 21 | 22 | Handlebars.registerHelper('helperMissing', function(arg) { 23 | if(arguments.length === 2) { 24 | return undefined; 25 | } else { 26 | throw new Error("Could not find property '" + arg + "'"); 27 | } 28 | }); 29 | 30 | var toString = Object.prototype.toString, functionType = "[object Function]"; 31 | 32 | Handlebars.registerHelper('blockHelperMissing', function(context, options) { 33 | var inverse = options.inverse || function() {}, fn = options.fn; 34 | 35 | 36 | var ret = ""; 37 | var type = toString.call(context); 38 | 39 | if(type === functionType) { context = context.call(this); } 40 | 41 | if(context === true) { 42 | return fn(this); 43 | } else if(context === false || context == null) { 44 | return inverse(this); 45 | } else if(type === "[object Array]") { 46 | if(context.length > 0) { 47 | return Handlebars.helpers.each(context, options); 48 | } else { 49 | return inverse(this); 50 | } 51 | } else { 52 | return fn(context); 53 | } 54 | }); 55 | 56 | Handlebars.K = function() {}; 57 | 58 | Handlebars.createFrame = Object.create || function(object) { 59 | Handlebars.K.prototype = object; 60 | var obj = new Handlebars.K(); 61 | Handlebars.K.prototype = null; 62 | return obj; 63 | }; 64 | 65 | Handlebars.logger = { 66 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, 67 | 68 | methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, 69 | 70 | // can be overridden in the host environment 71 | log: function(level, obj) { 72 | if (Handlebars.logger.level <= level) { 73 | var method = Handlebars.logger.methodMap[level]; 74 | if (typeof console !== 'undefined' && console[method]) { 75 | console[method].call(console, obj); 76 | } 77 | } 78 | } 79 | }; 80 | 81 | Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; 82 | 83 | Handlebars.registerHelper('each', function(context, options) { 84 | var fn = options.fn, inverse = options.inverse; 85 | var i = 0, ret = "", data; 86 | 87 | if (options.data) { 88 | data = Handlebars.createFrame(options.data); 89 | } 90 | 91 | if(context && typeof context === 'object') { 92 | if(context instanceof Array){ 93 | for(var j = context.length; i 2) { 301 | expected.push("'" + this.terminals_[p] + "'"); 302 | } 303 | if (this.lexer.showPosition) { 304 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; 305 | } else { 306 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); 307 | } 308 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 309 | } 310 | } 311 | if (action[0] instanceof Array && action.length > 1) { 312 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); 313 | } 314 | switch (action[0]) { 315 | case 1: 316 | stack.push(symbol); 317 | vstack.push(this.lexer.yytext); 318 | lstack.push(this.lexer.yylloc); 319 | stack.push(action[1]); 320 | symbol = null; 321 | if (!preErrorSymbol) { 322 | yyleng = this.lexer.yyleng; 323 | yytext = this.lexer.yytext; 324 | yylineno = this.lexer.yylineno; 325 | yyloc = this.lexer.yylloc; 326 | if (recovering > 0) 327 | recovering--; 328 | } else { 329 | symbol = preErrorSymbol; 330 | preErrorSymbol = null; 331 | } 332 | break; 333 | case 2: 334 | len = this.productions_[action[1]][1]; 335 | yyval.$ = vstack[vstack.length - len]; 336 | yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; 337 | if (ranges) { 338 | yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; 339 | } 340 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 341 | if (typeof r !== "undefined") { 342 | return r; 343 | } 344 | if (len) { 345 | stack = stack.slice(0, -1 * len * 2); 346 | vstack = vstack.slice(0, -1 * len); 347 | lstack = lstack.slice(0, -1 * len); 348 | } 349 | stack.push(this.productions_[action[1]][0]); 350 | vstack.push(yyval.$); 351 | lstack.push(yyval._$); 352 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; 353 | stack.push(newState); 354 | break; 355 | case 3: 356 | return true; 357 | } 358 | } 359 | return true; 360 | } 361 | }; 362 | /* Jison generated lexer */ 363 | var lexer = (function(){ 364 | var lexer = ({EOF:1, 365 | parseError:function parseError(str, hash) { 366 | if (this.yy.parser) { 367 | this.yy.parser.parseError(str, hash); 368 | } else { 369 | throw new Error(str); 370 | } 371 | }, 372 | setInput:function (input) { 373 | this._input = input; 374 | this._more = this._less = this.done = false; 375 | this.yylineno = this.yyleng = 0; 376 | this.yytext = this.matched = this.match = ''; 377 | this.conditionStack = ['INITIAL']; 378 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 379 | if (this.options.ranges) this.yylloc.range = [0,0]; 380 | this.offset = 0; 381 | return this; 382 | }, 383 | input:function () { 384 | var ch = this._input[0]; 385 | this.yytext += ch; 386 | this.yyleng++; 387 | this.offset++; 388 | this.match += ch; 389 | this.matched += ch; 390 | var lines = ch.match(/(?:\r\n?|\n).*/g); 391 | if (lines) { 392 | this.yylineno++; 393 | this.yylloc.last_line++; 394 | } else { 395 | this.yylloc.last_column++; 396 | } 397 | if (this.options.ranges) this.yylloc.range[1]++; 398 | 399 | this._input = this._input.slice(1); 400 | return ch; 401 | }, 402 | unput:function (ch) { 403 | var len = ch.length; 404 | var lines = ch.split(/(?:\r\n?|\n)/g); 405 | 406 | this._input = ch + this._input; 407 | this.yytext = this.yytext.substr(0, this.yytext.length-len-1); 408 | //this.yyleng -= len; 409 | this.offset -= len; 410 | var oldLines = this.match.split(/(?:\r\n?|\n)/g); 411 | this.match = this.match.substr(0, this.match.length-1); 412 | this.matched = this.matched.substr(0, this.matched.length-1); 413 | 414 | if (lines.length-1) this.yylineno -= lines.length-1; 415 | var r = this.yylloc.range; 416 | 417 | this.yylloc = {first_line: this.yylloc.first_line, 418 | last_line: this.yylineno+1, 419 | first_column: this.yylloc.first_column, 420 | last_column: lines ? 421 | (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: 422 | this.yylloc.first_column - len 423 | }; 424 | 425 | if (this.options.ranges) { 426 | this.yylloc.range = [r[0], r[0] + this.yyleng - len]; 427 | } 428 | return this; 429 | }, 430 | more:function () { 431 | this._more = true; 432 | return this; 433 | }, 434 | less:function (n) { 435 | this.unput(this.match.slice(n)); 436 | }, 437 | pastInput:function () { 438 | var past = this.matched.substr(0, this.matched.length - this.match.length); 439 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 440 | }, 441 | upcomingInput:function () { 442 | var next = this.match; 443 | if (next.length < 20) { 444 | next += this._input.substr(0, 20-next.length); 445 | } 446 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 447 | }, 448 | showPosition:function () { 449 | var pre = this.pastInput(); 450 | var c = new Array(pre.length + 1).join("-"); 451 | return pre + this.upcomingInput() + "\n" + c+"^"; 452 | }, 453 | next:function () { 454 | if (this.done) { 455 | return this.EOF; 456 | } 457 | if (!this._input) this.done = true; 458 | 459 | var token, 460 | match, 461 | tempMatch, 462 | index, 463 | col, 464 | lines; 465 | if (!this._more) { 466 | this.yytext = ''; 467 | this.match = ''; 468 | } 469 | var rules = this._currentRules(); 470 | for (var i=0;i < rules.length; i++) { 471 | tempMatch = this._input.match(this.rules[rules[i]]); 472 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 473 | match = tempMatch; 474 | index = i; 475 | if (!this.options.flex) break; 476 | } 477 | } 478 | if (match) { 479 | lines = match[0].match(/(?:\r\n?|\n).*/g); 480 | if (lines) this.yylineno += lines.length; 481 | this.yylloc = {first_line: this.yylloc.last_line, 482 | last_line: this.yylineno+1, 483 | first_column: this.yylloc.last_column, 484 | last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; 485 | this.yytext += match[0]; 486 | this.match += match[0]; 487 | this.matches = match; 488 | this.yyleng = this.yytext.length; 489 | if (this.options.ranges) { 490 | this.yylloc.range = [this.offset, this.offset += this.yyleng]; 491 | } 492 | this._more = false; 493 | this._input = this._input.slice(match[0].length); 494 | this.matched += match[0]; 495 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); 496 | if (this.done && this._input) this.done = false; 497 | if (token) return token; 498 | else return; 499 | } 500 | if (this._input === "") { 501 | return this.EOF; 502 | } else { 503 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 504 | {text: "", token: null, line: this.yylineno}); 505 | } 506 | }, 507 | lex:function lex() { 508 | var r = this.next(); 509 | if (typeof r !== 'undefined') { 510 | return r; 511 | } else { 512 | return this.lex(); 513 | } 514 | }, 515 | begin:function begin(condition) { 516 | this.conditionStack.push(condition); 517 | }, 518 | popState:function popState() { 519 | return this.conditionStack.pop(); 520 | }, 521 | _currentRules:function _currentRules() { 522 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 523 | }, 524 | topState:function () { 525 | return this.conditionStack[this.conditionStack.length-2]; 526 | }, 527 | pushState:function begin(condition) { 528 | this.begin(condition); 529 | }}); 530 | lexer.options = {}; 531 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 532 | 533 | var YYSTATE=YY_START 534 | switch($avoiding_name_collisions) { 535 | case 0: 536 | if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); 537 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); 538 | if(yy_.yytext) return 14; 539 | 540 | break; 541 | case 1: return 14; 542 | break; 543 | case 2: 544 | if(yy_.yytext.slice(-1) !== "\\") this.popState(); 545 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); 546 | return 14; 547 | 548 | break; 549 | case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; 550 | break; 551 | case 4: this.begin("par"); return 24; 552 | break; 553 | case 5: return 16; 554 | break; 555 | case 6: return 20; 556 | break; 557 | case 7: return 19; 558 | break; 559 | case 8: return 19; 560 | break; 561 | case 9: return 23; 562 | break; 563 | case 10: return 23; 564 | break; 565 | case 11: this.popState(); this.begin('com'); 566 | break; 567 | case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; 568 | break; 569 | case 13: return 22; 570 | break; 571 | case 14: return 36; 572 | break; 573 | case 15: return 35; 574 | break; 575 | case 16: return 35; 576 | break; 577 | case 17: return 39; 578 | break; 579 | case 18: /*ignore whitespace*/ 580 | break; 581 | case 19: this.popState(); return 18; 582 | break; 583 | case 20: this.popState(); return 18; 584 | break; 585 | case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; 586 | break; 587 | case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; 588 | break; 589 | case 23: yy_.yytext = yy_.yytext.substr(1); return 28; 590 | break; 591 | case 24: return 32; 592 | break; 593 | case 25: return 32; 594 | break; 595 | case 26: return 31; 596 | break; 597 | case 27: return 35; 598 | break; 599 | case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; 600 | break; 601 | case 29: return 'INVALID'; 602 | break; 603 | case 30: /*ignore whitespace*/ 604 | break; 605 | case 31: this.popState(); return 37; 606 | break; 607 | case 32: return 5; 608 | break; 609 | } 610 | }; 611 | lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/]; 612 | lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"par":{"rules":[30,31],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}}; 613 | return lexer;})() 614 | parser.lexer = lexer; 615 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; 616 | return new Parser; 617 | })();; 618 | // lib/handlebars/compiler/base.js 619 | Handlebars.Parser = handlebars; 620 | 621 | Handlebars.parse = function(string) { 622 | Handlebars.Parser.yy = Handlebars.AST; 623 | return Handlebars.Parser.parse(string); 624 | }; 625 | 626 | Handlebars.print = function(ast) { 627 | return new Handlebars.PrintVisitor().accept(ast); 628 | };; 629 | // lib/handlebars/compiler/ast.js 630 | (function() { 631 | 632 | Handlebars.AST = {}; 633 | 634 | Handlebars.AST.ProgramNode = function(statements, inverse) { 635 | this.type = "program"; 636 | this.statements = statements; 637 | if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } 638 | }; 639 | 640 | Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) { 641 | this.type = "mustache"; 642 | this.escaped = !unescaped; 643 | this.hash = hash; 644 | 645 | var id = this.id = rawParams[0]; 646 | var params = this.params = rawParams.slice(1); 647 | 648 | // a mustache is an eligible helper if: 649 | // * its id is simple (a single part, not `this` or `..`) 650 | var eligibleHelper = this.eligibleHelper = id.isSimple; 651 | 652 | // a mustache is definitely a helper if: 653 | // * it is an eligible helper, and 654 | // * it has at least one parameter or hash segment 655 | this.isHelper = eligibleHelper && (params.length || hash); 656 | 657 | // if a mustache is an eligible helper but not a definite 658 | // helper, it is ambiguous, and will be resolved in a later 659 | // pass or at runtime. 660 | }; 661 | 662 | Handlebars.AST.PartialNode = function(partialName, context) { 663 | this.type = "partial"; 664 | this.partialName = partialName; 665 | this.context = context; 666 | }; 667 | 668 | var verifyMatch = function(open, close) { 669 | if(open.original !== close.original) { 670 | throw new Handlebars.Exception(open.original + " doesn't match " + close.original); 671 | } 672 | }; 673 | 674 | Handlebars.AST.BlockNode = function(mustache, program, inverse, close) { 675 | verifyMatch(mustache.id, close); 676 | this.type = "block"; 677 | this.mustache = mustache; 678 | this.program = program; 679 | this.inverse = inverse; 680 | 681 | if (this.inverse && !this.program) { 682 | this.isInverse = true; 683 | } 684 | }; 685 | 686 | Handlebars.AST.ContentNode = function(string) { 687 | this.type = "content"; 688 | this.string = string; 689 | }; 690 | 691 | Handlebars.AST.HashNode = function(pairs) { 692 | this.type = "hash"; 693 | this.pairs = pairs; 694 | }; 695 | 696 | Handlebars.AST.IdNode = function(parts) { 697 | this.type = "ID"; 698 | this.original = parts.join("."); 699 | 700 | var dig = [], depth = 0; 701 | 702 | for(var i=0,l=parts.length; i": ">", 782 | '"': """, 783 | "'": "'", 784 | "`": "`" 785 | }; 786 | 787 | var badChars = /[&<>"'`]/g; 788 | var possible = /[&<>"'`]/; 789 | 790 | var escapeChar = function(chr) { 791 | return escape[chr] || "&"; 792 | }; 793 | 794 | Handlebars.Utils = { 795 | escapeExpression: function(string) { 796 | // don't escape SafeStrings, since they're already safe 797 | if (string instanceof Handlebars.SafeString) { 798 | return string.toString(); 799 | } else if (string == null || string === false) { 800 | return ""; 801 | } 802 | 803 | if(!possible.test(string)) { return string; } 804 | return string.replace(badChars, escapeChar); 805 | }, 806 | 807 | isEmpty: function(value) { 808 | if (!value && value !== 0) { 809 | return true; 810 | } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) { 811 | return true; 812 | } else { 813 | return false; 814 | } 815 | } 816 | }; 817 | })();; 818 | // lib/handlebars/compiler/compiler.js 819 | 820 | /*jshint eqnull:true*/ 821 | Handlebars.Compiler = function() {}; 822 | Handlebars.JavaScriptCompiler = function() {}; 823 | 824 | (function(Compiler, JavaScriptCompiler) { 825 | // the foundHelper register will disambiguate helper lookup from finding a 826 | // function in a context. This is necessary for mustache compatibility, which 827 | // requires that context functions in blocks are evaluated by blockHelperMissing, 828 | // and then proceed as if the resulting value was provided to blockHelperMissing. 829 | 830 | Compiler.prototype = { 831 | compiler: Compiler, 832 | 833 | disassemble: function() { 834 | var opcodes = this.opcodes, opcode, out = [], params, param; 835 | 836 | for (var i=0, l=opcodes.length; i 0) { 1295 | this.source[1] = this.source[1] + ", " + locals.join(", "); 1296 | } 1297 | 1298 | // Generate minimizer alias mappings 1299 | if (!this.isChild) { 1300 | var aliases = []; 1301 | for (var alias in this.context.aliases) { 1302 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; 1303 | } 1304 | } 1305 | 1306 | if (this.source[1]) { 1307 | this.source[1] = "var " + this.source[1].substring(2) + ";"; 1308 | } 1309 | 1310 | // Merge children 1311 | if (!this.isChild) { 1312 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; 1313 | } 1314 | 1315 | if (!this.environment.isSimple) { 1316 | this.source.push("return buffer;"); 1317 | } 1318 | 1319 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; 1320 | 1321 | for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } 1759 | return "stack" + this.stackSlot; 1760 | }, 1761 | 1762 | popStack: function() { 1763 | var item = this.compileStack.pop(); 1764 | 1765 | if (item instanceof Literal) { 1766 | return item.value; 1767 | } else { 1768 | this.stackSlot--; 1769 | return item; 1770 | } 1771 | }, 1772 | 1773 | topStack: function() { 1774 | var item = this.compileStack[this.compileStack.length - 1]; 1775 | 1776 | if (item instanceof Literal) { 1777 | return item.value; 1778 | } else { 1779 | return item; 1780 | } 1781 | }, 1782 | 1783 | quotedString: function(str) { 1784 | return '"' + str 1785 | .replace(/\\/g, '\\\\') 1786 | .replace(/"/g, '\\"') 1787 | .replace(/\n/g, '\\n') 1788 | .replace(/\r/g, '\\r') + '"'; 1789 | }, 1790 | 1791 | setupHelper: function(paramSize, name) { 1792 | var params = []; 1793 | this.setupParams(paramSize, params); 1794 | var foundHelper = this.nameLookup('helpers', name, 'helper'); 1795 | 1796 | return { 1797 | params: params, 1798 | name: foundHelper, 1799 | callParams: ["depth0"].concat(params).join(", "), 1800 | helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ") 1801 | }; 1802 | }, 1803 | 1804 | // the params and contexts arguments are passed in arrays 1805 | // to fill in 1806 | setupParams: function(paramSize, params) { 1807 | var options = [], contexts = [], types = [], param, inverse, program; 1808 | 1809 | options.push("hash:" + this.popStack()); 1810 | 1811 | inverse = this.popStack(); 1812 | program = this.popStack(); 1813 | 1814 | // Avoid setting fn and inverse if neither are set. This allows 1815 | // helpers to do a check for `if (options.fn)` 1816 | if (program || inverse) { 1817 | if (!program) { 1818 | this.context.aliases.self = "this"; 1819 | program = "self.noop"; 1820 | } 1821 | 1822 | if (!inverse) { 1823 | this.context.aliases.self = "this"; 1824 | inverse = "self.noop"; 1825 | } 1826 | 1827 | options.push("inverse:" + inverse); 1828 | options.push("fn:" + program); 1829 | } 1830 | 1831 | for(var i=0; i(" + bad + ", " + good + ", " + this.assertions.length + ")"; 217 | 218 | addEvent(b, "click", function() { 219 | var next = b.nextSibling.nextSibling, 220 | display = next.style.display; 221 | next.style.display = display === "none" ? "block" : "none"; 222 | }); 223 | 224 | addEvent(b, "dblclick", function( e ) { 225 | var target = e && e.target ? e.target : window.event.srcElement; 226 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { 227 | target = target.parentNode; 228 | } 229 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 230 | window.location = QUnit.url({ testNumber: test.testNumber }); 231 | } 232 | }); 233 | 234 | // `li` initialized at top of scope 235 | li = id( this.id ); 236 | li.className = bad ? "fail" : "pass"; 237 | li.removeChild( li.firstChild ); 238 | a = li.firstChild; 239 | li.appendChild( b ); 240 | li.appendChild ( a ); 241 | li.appendChild( ol ); 242 | 243 | } else { 244 | for ( i = 0; i < this.assertions.length; i++ ) { 245 | if ( !this.assertions[i].result ) { 246 | bad++; 247 | config.stats.bad++; 248 | config.moduleStats.bad++; 249 | } 250 | } 251 | } 252 | 253 | runLoggingCallbacks( "testDone", QUnit, { 254 | name: this.testName, 255 | module: this.module, 256 | failed: bad, 257 | passed: this.assertions.length - bad, 258 | total: this.assertions.length 259 | }); 260 | 261 | QUnit.reset(); 262 | 263 | config.current = undefined; 264 | }, 265 | 266 | queue: function() { 267 | var bad, 268 | test = this; 269 | 270 | synchronize(function() { 271 | test.init(); 272 | }); 273 | function run() { 274 | // each of these can by async 275 | synchronize(function() { 276 | test.setup(); 277 | }); 278 | synchronize(function() { 279 | test.run(); 280 | }); 281 | synchronize(function() { 282 | test.teardown(); 283 | }); 284 | synchronize(function() { 285 | test.finish(); 286 | }); 287 | } 288 | 289 | // `bad` initialized at top of scope 290 | // defer when previous test run passed, if storage is available 291 | bad = QUnit.config.reorder && defined.sessionStorage && 292 | +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); 293 | 294 | if ( bad ) { 295 | run(); 296 | } else { 297 | synchronize( run, true ); 298 | } 299 | } 300 | }; 301 | 302 | // Root QUnit object. 303 | // `QUnit` initialized at top of scope 304 | QUnit = { 305 | 306 | // call on start of module test to prepend name to all tests 307 | module: function( name, testEnvironment ) { 308 | config.currentModule = name; 309 | config.currentModuleTestEnvironment = testEnvironment; 310 | config.modules[name] = true; 311 | }, 312 | 313 | asyncTest: function( testName, expected, callback ) { 314 | if ( arguments.length === 2 ) { 315 | callback = expected; 316 | expected = null; 317 | } 318 | 319 | QUnit.test( testName, expected, callback, true ); 320 | }, 321 | 322 | test: function( testName, expected, callback, async ) { 323 | var test, 324 | name = "" + escapeInnerText( testName ) + ""; 325 | 326 | if ( arguments.length === 2 ) { 327 | callback = expected; 328 | expected = null; 329 | } 330 | 331 | if ( config.currentModule ) { 332 | name = "" + config.currentModule + ": " + name; 333 | } 334 | 335 | test = new Test({ 336 | name: name, 337 | testName: testName, 338 | expected: expected, 339 | async: async, 340 | callback: callback, 341 | module: config.currentModule, 342 | moduleTestEnvironment: config.currentModuleTestEnvironment, 343 | stack: sourceFromStacktrace( 2 ) 344 | }); 345 | 346 | if ( !validTest( test ) ) { 347 | return; 348 | } 349 | 350 | test.queue(); 351 | }, 352 | 353 | // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 354 | expect: function( asserts ) { 355 | if (arguments.length === 1) { 356 | config.current.expected = asserts; 357 | } else { 358 | return config.current.expected; 359 | } 360 | }, 361 | 362 | start: function( count ) { 363 | config.semaphore -= count || 1; 364 | // don't start until equal number of stop-calls 365 | if ( config.semaphore > 0 ) { 366 | return; 367 | } 368 | // ignore if start is called more often then stop 369 | if ( config.semaphore < 0 ) { 370 | config.semaphore = 0; 371 | } 372 | // A slight delay, to avoid any current callbacks 373 | if ( defined.setTimeout ) { 374 | window.setTimeout(function() { 375 | if ( config.semaphore > 0 ) { 376 | return; 377 | } 378 | if ( config.timeout ) { 379 | clearTimeout( config.timeout ); 380 | } 381 | 382 | config.blocking = false; 383 | process( true ); 384 | }, 13); 385 | } else { 386 | config.blocking = false; 387 | process( true ); 388 | } 389 | }, 390 | 391 | stop: function( count ) { 392 | config.semaphore += count || 1; 393 | config.blocking = true; 394 | 395 | if ( config.testTimeout && defined.setTimeout ) { 396 | clearTimeout( config.timeout ); 397 | config.timeout = window.setTimeout(function() { 398 | QUnit.ok( false, "Test timed out" ); 399 | config.semaphore = 1; 400 | QUnit.start(); 401 | }, config.testTimeout ); 402 | } 403 | } 404 | }; 405 | 406 | // Asssert helpers 407 | // All of these must call either QUnit.push() or manually do: 408 | // - runLoggingCallbacks( "log", .. ); 409 | // - config.current.assertions.push({ .. }); 410 | QUnit.assert = { 411 | /** 412 | * Asserts rough true-ish result. 413 | * @name ok 414 | * @function 415 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 416 | */ 417 | ok: function( result, msg ) { 418 | if ( !config.current ) { 419 | throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); 420 | } 421 | result = !!result; 422 | 423 | var source, 424 | details = { 425 | module: config.current.module, 426 | name: config.current.testName, 427 | result: result, 428 | message: msg 429 | }; 430 | 431 | msg = escapeInnerText( msg || (result ? "okay" : "failed" ) ); 432 | msg = "" + msg + ""; 433 | 434 | if ( !result ) { 435 | source = sourceFromStacktrace( 2 ); 436 | if ( source ) { 437 | details.source = source; 438 | msg += "
Source:
" + escapeInnerText( source ) + "
"; 439 | } 440 | } 441 | runLoggingCallbacks( "log", QUnit, details ); 442 | config.current.assertions.push({ 443 | result: result, 444 | message: msg 445 | }); 446 | }, 447 | 448 | /** 449 | * Assert that the first two arguments are equal, with an optional message. 450 | * Prints out both actual and expected values. 451 | * @name equal 452 | * @function 453 | * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); 454 | */ 455 | equal: function( actual, expected, message ) { 456 | QUnit.push( expected == actual, actual, expected, message ); 457 | }, 458 | 459 | /** 460 | * @name notEqual 461 | * @function 462 | */ 463 | notEqual: function( actual, expected, message ) { 464 | QUnit.push( expected != actual, actual, expected, message ); 465 | }, 466 | 467 | /** 468 | * @name deepEqual 469 | * @function 470 | */ 471 | deepEqual: function( actual, expected, message ) { 472 | QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); 473 | }, 474 | 475 | /** 476 | * @name notDeepEqual 477 | * @function 478 | */ 479 | notDeepEqual: function( actual, expected, message ) { 480 | QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); 481 | }, 482 | 483 | /** 484 | * @name strictEqual 485 | * @function 486 | */ 487 | strictEqual: function( actual, expected, message ) { 488 | QUnit.push( expected === actual, actual, expected, message ); 489 | }, 490 | 491 | /** 492 | * @name notStrictEqual 493 | * @function 494 | */ 495 | notStrictEqual: function( actual, expected, message ) { 496 | QUnit.push( expected !== actual, actual, expected, message ); 497 | }, 498 | 499 | throws: function( block, expected, message ) { 500 | var actual, 501 | ok = false; 502 | 503 | // 'expected' is optional 504 | if ( typeof expected === "string" ) { 505 | message = expected; 506 | expected = null; 507 | } 508 | 509 | config.current.ignoreGlobalErrors = true; 510 | try { 511 | block.call( config.current.testEnvironment ); 512 | } catch (e) { 513 | actual = e; 514 | } 515 | config.current.ignoreGlobalErrors = false; 516 | 517 | if ( actual ) { 518 | // we don't want to validate thrown error 519 | if ( !expected ) { 520 | ok = true; 521 | // expected is a regexp 522 | } else if ( QUnit.objectType( expected ) === "regexp" ) { 523 | ok = expected.test( actual ); 524 | // expected is a constructor 525 | } else if ( actual instanceof expected ) { 526 | ok = true; 527 | // expected is a validation function which returns true is validation passed 528 | } else if ( expected.call( {}, actual ) === true ) { 529 | ok = true; 530 | } 531 | 532 | QUnit.push( ok, actual, null, message ); 533 | } else { 534 | QUnit.pushFailure( message, null, 'No exception was thrown.' ); 535 | } 536 | } 537 | }; 538 | 539 | /** 540 | * @deprecate since 1.8.0 541 | * Kept assertion helpers in root for backwards compatibility 542 | */ 543 | extend( QUnit, QUnit.assert ); 544 | 545 | /** 546 | * @deprecated since 1.9.0 547 | * Kept global "raises()" for backwards compatibility 548 | */ 549 | QUnit.raises = QUnit.assert.throws; 550 | 551 | /** 552 | * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 553 | * Kept to avoid TypeErrors for undefined methods. 554 | */ 555 | QUnit.equals = function() { 556 | QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); 557 | }; 558 | QUnit.same = function() { 559 | QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); 560 | }; 561 | 562 | // We want access to the constructor's prototype 563 | (function() { 564 | function F() {} 565 | F.prototype = QUnit; 566 | QUnit = new F(); 567 | // Make F QUnit's constructor so that we can add to the prototype later 568 | QUnit.constructor = F; 569 | }()); 570 | 571 | /** 572 | * Config object: Maintain internal state 573 | * Later exposed as QUnit.config 574 | * `config` initialized at top of scope 575 | */ 576 | config = { 577 | // The queue of tests to run 578 | queue: [], 579 | 580 | // block until document ready 581 | blocking: true, 582 | 583 | // when enabled, show only failing tests 584 | // gets persisted through sessionStorage and can be changed in UI via checkbox 585 | hidepassed: false, 586 | 587 | // by default, run previously failed tests first 588 | // very useful in combination with "Hide passed tests" checked 589 | reorder: true, 590 | 591 | // by default, modify document.title when suite is done 592 | altertitle: true, 593 | 594 | // when enabled, all tests must call expect() 595 | requireExpects: false, 596 | 597 | // add checkboxes that are persisted in the query-string 598 | // when enabled, the id is set to `true` as a `QUnit.config` property 599 | urlConfig: [ 600 | { 601 | id: "noglobals", 602 | label: "Check for Globals", 603 | tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." 604 | }, 605 | { 606 | id: "notrycatch", 607 | label: "No try-catch", 608 | tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." 609 | } 610 | ], 611 | 612 | // Set of all modules. 613 | modules: {}, 614 | 615 | // logging callback queues 616 | begin: [], 617 | done: [], 618 | log: [], 619 | testStart: [], 620 | testDone: [], 621 | moduleStart: [], 622 | moduleDone: [] 623 | }; 624 | 625 | // Initialize more QUnit.config and QUnit.urlParams 626 | (function() { 627 | var i, 628 | location = window.location || { search: "", protocol: "file:" }, 629 | params = location.search.slice( 1 ).split( "&" ), 630 | length = params.length, 631 | urlParams = {}, 632 | current; 633 | 634 | if ( params[ 0 ] ) { 635 | for ( i = 0; i < length; i++ ) { 636 | current = params[ i ].split( "=" ); 637 | current[ 0 ] = decodeURIComponent( current[ 0 ] ); 638 | // allow just a key to turn on a flag, e.g., test.html?noglobals 639 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; 640 | urlParams[ current[ 0 ] ] = current[ 1 ]; 641 | } 642 | } 643 | 644 | QUnit.urlParams = urlParams; 645 | 646 | // String search anywhere in moduleName+testName 647 | config.filter = urlParams.filter; 648 | 649 | // Exact match of the module name 650 | config.module = urlParams.module; 651 | 652 | config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; 653 | 654 | // Figure out if we're running the tests from a server or not 655 | QUnit.isLocal = location.protocol === "file:"; 656 | }()); 657 | 658 | // Export global variables, unless an 'exports' object exists, 659 | // in that case we assume we're in CommonJS (dealt with on the bottom of the script) 660 | if ( typeof exports === "undefined" ) { 661 | extend( window, QUnit ); 662 | 663 | // Expose QUnit object 664 | window.QUnit = QUnit; 665 | } 666 | 667 | // Extend QUnit object, 668 | // these after set here because they should not be exposed as global functions 669 | extend( QUnit, { 670 | config: config, 671 | 672 | // Initialize the configuration options 673 | init: function() { 674 | extend( config, { 675 | stats: { all: 0, bad: 0 }, 676 | moduleStats: { all: 0, bad: 0 }, 677 | started: +new Date(), 678 | updateRate: 1000, 679 | blocking: false, 680 | autostart: true, 681 | autorun: false, 682 | filter: "", 683 | queue: [], 684 | semaphore: 0 685 | }); 686 | 687 | var tests, banner, result, 688 | qunit = id( "qunit" ); 689 | 690 | if ( qunit ) { 691 | qunit.innerHTML = 692 | "

" + escapeInnerText( document.title ) + "

" + 693 | "

" + 694 | "
" + 695 | "

" + 696 | "
    "; 697 | } 698 | 699 | tests = id( "qunit-tests" ); 700 | banner = id( "qunit-banner" ); 701 | result = id( "qunit-testresult" ); 702 | 703 | if ( tests ) { 704 | tests.innerHTML = ""; 705 | } 706 | 707 | if ( banner ) { 708 | banner.className = ""; 709 | } 710 | 711 | if ( result ) { 712 | result.parentNode.removeChild( result ); 713 | } 714 | 715 | if ( tests ) { 716 | result = document.createElement( "p" ); 717 | result.id = "qunit-testresult"; 718 | result.className = "result"; 719 | tests.parentNode.insertBefore( result, tests ); 720 | result.innerHTML = "Running...
     "; 721 | } 722 | }, 723 | 724 | // Resets the test setup. Useful for tests that modify the DOM. 725 | reset: function() { 726 | var fixture = id( "qunit-fixture" ); 727 | if ( fixture ) { 728 | fixture.innerHTML = config.fixture; 729 | } 730 | }, 731 | 732 | // Trigger an event on an element. 733 | // @example triggerEvent( document.body, "click" ); 734 | triggerEvent: function( elem, type, event ) { 735 | if ( document.createEvent ) { 736 | event = document.createEvent( "MouseEvents" ); 737 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 738 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 739 | 740 | elem.dispatchEvent( event ); 741 | } else if ( elem.fireEvent ) { 742 | elem.fireEvent( "on" + type ); 743 | } 744 | }, 745 | 746 | // Safe object type checking 747 | is: function( type, obj ) { 748 | return QUnit.objectType( obj ) == type; 749 | }, 750 | 751 | objectType: function( obj ) { 752 | if ( typeof obj === "undefined" ) { 753 | return "undefined"; 754 | // consider: typeof null === object 755 | } 756 | if ( obj === null ) { 757 | return "null"; 758 | } 759 | 760 | var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ""; 761 | 762 | switch ( type ) { 763 | case "Number": 764 | if ( isNaN(obj) ) { 765 | return "nan"; 766 | } 767 | return "number"; 768 | case "String": 769 | case "Boolean": 770 | case "Array": 771 | case "Date": 772 | case "RegExp": 773 | case "Function": 774 | return type.toLowerCase(); 775 | } 776 | if ( typeof obj === "object" ) { 777 | return "object"; 778 | } 779 | return undefined; 780 | }, 781 | 782 | push: function( result, actual, expected, message ) { 783 | if ( !config.current ) { 784 | throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); 785 | } 786 | 787 | var output, source, 788 | details = { 789 | module: config.current.module, 790 | name: config.current.testName, 791 | result: result, 792 | message: message, 793 | actual: actual, 794 | expected: expected 795 | }; 796 | 797 | message = escapeInnerText( message ) || ( result ? "okay" : "failed" ); 798 | message = "" + message + ""; 799 | output = message; 800 | 801 | if ( !result ) { 802 | expected = escapeInnerText( QUnit.jsDump.parse(expected) ); 803 | actual = escapeInnerText( QUnit.jsDump.parse(actual) ); 804 | output += ""; 805 | 806 | if ( actual != expected ) { 807 | output += ""; 808 | output += ""; 809 | } 810 | 811 | source = sourceFromStacktrace(); 812 | 813 | if ( source ) { 814 | details.source = source; 815 | output += ""; 816 | } 817 | 818 | output += "
    Expected:
    " + expected + "
    Result:
    " + actual + "
    Diff:
    " + QUnit.diff( expected, actual ) + "
    Source:
    " + escapeInnerText( source ) + "
    "; 819 | } 820 | 821 | runLoggingCallbacks( "log", QUnit, details ); 822 | 823 | config.current.assertions.push({ 824 | result: !!result, 825 | message: output 826 | }); 827 | }, 828 | 829 | pushFailure: function( message, source, actual ) { 830 | if ( !config.current ) { 831 | throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); 832 | } 833 | 834 | var output, 835 | details = { 836 | module: config.current.module, 837 | name: config.current.testName, 838 | result: false, 839 | message: message 840 | }; 841 | 842 | message = escapeInnerText( message ) || "error"; 843 | message = "" + message + ""; 844 | output = message; 845 | 846 | output += ""; 847 | 848 | if ( actual ) { 849 | output += ""; 850 | } 851 | 852 | if ( source ) { 853 | details.source = source; 854 | output += ""; 855 | } 856 | 857 | output += "
    Result:
    " + escapeInnerText( actual ) + "
    Source:
    " + escapeInnerText( source ) + "
    "; 858 | 859 | runLoggingCallbacks( "log", QUnit, details ); 860 | 861 | config.current.assertions.push({ 862 | result: false, 863 | message: output 864 | }); 865 | }, 866 | 867 | url: function( params ) { 868 | params = extend( extend( {}, QUnit.urlParams ), params ); 869 | var key, 870 | querystring = "?"; 871 | 872 | for ( key in params ) { 873 | if ( !hasOwn.call( params, key ) ) { 874 | continue; 875 | } 876 | querystring += encodeURIComponent( key ) + "=" + 877 | encodeURIComponent( params[ key ] ) + "&"; 878 | } 879 | return window.location.pathname + querystring.slice( 0, -1 ); 880 | }, 881 | 882 | extend: extend, 883 | id: id, 884 | addEvent: addEvent 885 | // load, equiv, jsDump, diff: Attached later 886 | }); 887 | 888 | /** 889 | * @deprecated: Created for backwards compatibility with test runner that set the hook function 890 | * into QUnit.{hook}, instead of invoking it and passing the hook function. 891 | * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. 892 | * Doing this allows us to tell if the following methods have been overwritten on the actual 893 | * QUnit object. 894 | */ 895 | extend( QUnit.constructor.prototype, { 896 | 897 | // Logging callbacks; all receive a single argument with the listed properties 898 | // run test/logs.html for any related changes 899 | begin: registerLoggingCallback( "begin" ), 900 | 901 | // done: { failed, passed, total, runtime } 902 | done: registerLoggingCallback( "done" ), 903 | 904 | // log: { result, actual, expected, message } 905 | log: registerLoggingCallback( "log" ), 906 | 907 | // testStart: { name } 908 | testStart: registerLoggingCallback( "testStart" ), 909 | 910 | // testDone: { name, failed, passed, total } 911 | testDone: registerLoggingCallback( "testDone" ), 912 | 913 | // moduleStart: { name } 914 | moduleStart: registerLoggingCallback( "moduleStart" ), 915 | 916 | // moduleDone: { name, failed, passed, total } 917 | moduleDone: registerLoggingCallback( "moduleDone" ) 918 | }); 919 | 920 | if ( typeof document === "undefined" || document.readyState === "complete" ) { 921 | config.autorun = true; 922 | } 923 | 924 | QUnit.load = function() { 925 | runLoggingCallbacks( "begin", QUnit, {} ); 926 | 927 | // Initialize the config, saving the execution queue 928 | var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter, 929 | numModules = 0, 930 | moduleFilterHtml = "", 931 | urlConfigHtml = "", 932 | oldconfig = extend( {}, config ); 933 | 934 | QUnit.init(); 935 | extend(config, oldconfig); 936 | 937 | config.blocking = false; 938 | 939 | len = config.urlConfig.length; 940 | 941 | for ( i = 0; i < len; i++ ) { 942 | val = config.urlConfig[i]; 943 | if ( typeof val === "string" ) { 944 | val = { 945 | id: val, 946 | label: val, 947 | tooltip: "[no tooltip available]" 948 | }; 949 | } 950 | config[ val.id ] = QUnit.urlParams[ val.id ]; 951 | urlConfigHtml += ""; 952 | } 953 | 954 | moduleFilterHtml += ""; 962 | 963 | // `userAgent` initialized at top of scope 964 | userAgent = id( "qunit-userAgent" ); 965 | if ( userAgent ) { 966 | userAgent.innerHTML = navigator.userAgent; 967 | } 968 | 969 | // `banner` initialized at top of scope 970 | banner = id( "qunit-header" ); 971 | if ( banner ) { 972 | banner.innerHTML = "" + banner.innerHTML + " "; 973 | } 974 | 975 | // `toolbar` initialized at top of scope 976 | toolbar = id( "qunit-testrunner-toolbar" ); 977 | if ( toolbar ) { 978 | // `filter` initialized at top of scope 979 | filter = document.createElement( "input" ); 980 | filter.type = "checkbox"; 981 | filter.id = "qunit-filter-pass"; 982 | 983 | addEvent( filter, "click", function() { 984 | var tmp, 985 | ol = document.getElementById( "qunit-tests" ); 986 | 987 | if ( filter.checked ) { 988 | ol.className = ol.className + " hidepass"; 989 | } else { 990 | tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; 991 | ol.className = tmp.replace( / hidepass /, " " ); 992 | } 993 | if ( defined.sessionStorage ) { 994 | if (filter.checked) { 995 | sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); 996 | } else { 997 | sessionStorage.removeItem( "qunit-filter-passed-tests" ); 998 | } 999 | } 1000 | }); 1001 | 1002 | if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { 1003 | filter.checked = true; 1004 | // `ol` initialized at top of scope 1005 | ol = document.getElementById( "qunit-tests" ); 1006 | ol.className = ol.className + " hidepass"; 1007 | } 1008 | toolbar.appendChild( filter ); 1009 | 1010 | // `label` initialized at top of scope 1011 | label = document.createElement( "label" ); 1012 | label.setAttribute( "for", "qunit-filter-pass" ); 1013 | label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." ); 1014 | label.innerHTML = "Hide passed tests"; 1015 | toolbar.appendChild( label ); 1016 | 1017 | urlConfigCheckboxes = document.createElement( 'span' ); 1018 | urlConfigCheckboxes.innerHTML = urlConfigHtml; 1019 | addEvent( urlConfigCheckboxes, "change", function( event ) { 1020 | var params = {}; 1021 | params[ event.target.name ] = event.target.checked ? true : undefined; 1022 | window.location = QUnit.url( params ); 1023 | }); 1024 | toolbar.appendChild( urlConfigCheckboxes ); 1025 | 1026 | if (numModules > 1) { 1027 | moduleFilter = document.createElement( 'span' ); 1028 | moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' ); 1029 | moduleFilter.innerHTML = moduleFilterHtml; 1030 | addEvent( moduleFilter, "change", function() { 1031 | var selectBox = moduleFilter.getElementsByTagName("select")[0], 1032 | selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); 1033 | 1034 | window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } ); 1035 | }); 1036 | toolbar.appendChild(moduleFilter); 1037 | } 1038 | } 1039 | 1040 | // `main` initialized at top of scope 1041 | main = id( "qunit-fixture" ); 1042 | if ( main ) { 1043 | config.fixture = main.innerHTML; 1044 | } 1045 | 1046 | if ( config.autostart ) { 1047 | QUnit.start(); 1048 | } 1049 | }; 1050 | 1051 | addEvent( window, "load", QUnit.load ); 1052 | 1053 | // `onErrorFnPrev` initialized at top of scope 1054 | // Preserve other handlers 1055 | onErrorFnPrev = window.onerror; 1056 | 1057 | // Cover uncaught exceptions 1058 | // Returning true will surpress the default browser handler, 1059 | // returning false will let it run. 1060 | window.onerror = function ( error, filePath, linerNr ) { 1061 | var ret = false; 1062 | if ( onErrorFnPrev ) { 1063 | ret = onErrorFnPrev( error, filePath, linerNr ); 1064 | } 1065 | 1066 | // Treat return value as window.onerror itself does, 1067 | // Only do our handling if not surpressed. 1068 | if ( ret !== true ) { 1069 | if ( QUnit.config.current ) { 1070 | if ( QUnit.config.current.ignoreGlobalErrors ) { 1071 | return true; 1072 | } 1073 | QUnit.pushFailure( error, filePath + ":" + linerNr ); 1074 | } else { 1075 | QUnit.test( "global failure", extend( function() { 1076 | QUnit.pushFailure( error, filePath + ":" + linerNr ); 1077 | }, { validTest: validTest } ) ); 1078 | } 1079 | return false; 1080 | } 1081 | 1082 | return ret; 1083 | }; 1084 | 1085 | function done() { 1086 | config.autorun = true; 1087 | 1088 | // Log the last module results 1089 | if ( config.currentModule ) { 1090 | runLoggingCallbacks( "moduleDone", QUnit, { 1091 | name: config.currentModule, 1092 | failed: config.moduleStats.bad, 1093 | passed: config.moduleStats.all - config.moduleStats.bad, 1094 | total: config.moduleStats.all 1095 | }); 1096 | } 1097 | 1098 | var i, key, 1099 | banner = id( "qunit-banner" ), 1100 | tests = id( "qunit-tests" ), 1101 | runtime = +new Date() - config.started, 1102 | passed = config.stats.all - config.stats.bad, 1103 | html = [ 1104 | "Tests completed in ", 1105 | runtime, 1106 | " milliseconds.
    ", 1107 | "", 1108 | passed, 1109 | " tests of ", 1110 | config.stats.all, 1111 | " passed, ", 1112 | config.stats.bad, 1113 | " failed." 1114 | ].join( "" ); 1115 | 1116 | if ( banner ) { 1117 | banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); 1118 | } 1119 | 1120 | if ( tests ) { 1121 | id( "qunit-testresult" ).innerHTML = html; 1122 | } 1123 | 1124 | if ( config.altertitle && typeof document !== "undefined" && document.title ) { 1125 | // show ✖ for good, ✔ for bad suite result in title 1126 | // use escape sequences in case file gets loaded with non-utf-8-charset 1127 | document.title = [ 1128 | ( config.stats.bad ? "\u2716" : "\u2714" ), 1129 | document.title.replace( /^[\u2714\u2716] /i, "" ) 1130 | ].join( " " ); 1131 | } 1132 | 1133 | // clear own sessionStorage items if all tests passed 1134 | if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { 1135 | // `key` & `i` initialized at top of scope 1136 | for ( i = 0; i < sessionStorage.length; i++ ) { 1137 | key = sessionStorage.key( i++ ); 1138 | if ( key.indexOf( "qunit-test-" ) === 0 ) { 1139 | sessionStorage.removeItem( key ); 1140 | } 1141 | } 1142 | } 1143 | 1144 | // scroll back to top to show results 1145 | if ( window.scrollTo ) { 1146 | window.scrollTo(0, 0); 1147 | } 1148 | 1149 | runLoggingCallbacks( "done", QUnit, { 1150 | failed: config.stats.bad, 1151 | passed: passed, 1152 | total: config.stats.all, 1153 | runtime: runtime 1154 | }); 1155 | } 1156 | 1157 | /** @return Boolean: true if this test should be ran */ 1158 | function validTest( test ) { 1159 | var include, 1160 | filter = config.filter && config.filter.toLowerCase(), 1161 | module = config.module && config.module.toLowerCase(), 1162 | fullName = (test.module + ": " + test.testName).toLowerCase(); 1163 | 1164 | // Internally-generated tests are always valid 1165 | if ( test.callback && test.callback.validTest === validTest ) { 1166 | delete test.callback.validTest; 1167 | return true; 1168 | } 1169 | 1170 | if ( config.testNumber ) { 1171 | return test.testNumber === config.testNumber; 1172 | } 1173 | 1174 | if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { 1175 | return false; 1176 | } 1177 | 1178 | if ( !filter ) { 1179 | return true; 1180 | } 1181 | 1182 | include = filter.charAt( 0 ) !== "!"; 1183 | if ( !include ) { 1184 | filter = filter.slice( 1 ); 1185 | } 1186 | 1187 | // If the filter matches, we need to honour include 1188 | if ( fullName.indexOf( filter ) !== -1 ) { 1189 | return include; 1190 | } 1191 | 1192 | // Otherwise, do the opposite 1193 | return !include; 1194 | } 1195 | 1196 | // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) 1197 | // Later Safari and IE10 are supposed to support error.stack as well 1198 | // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack 1199 | function extractStacktrace( e, offset ) { 1200 | offset = offset === undefined ? 3 : offset; 1201 | 1202 | var stack, include, i, regex; 1203 | 1204 | if ( e.stacktrace ) { 1205 | // Opera 1206 | return e.stacktrace.split( "\n" )[ offset + 3 ]; 1207 | } else if ( e.stack ) { 1208 | // Firefox, Chrome 1209 | stack = e.stack.split( "\n" ); 1210 | if (/^error$/i.test( stack[0] ) ) { 1211 | stack.shift(); 1212 | } 1213 | if ( fileName ) { 1214 | include = []; 1215 | for ( i = offset; i < stack.length; i++ ) { 1216 | if ( stack[ i ].indexOf( fileName ) != -1 ) { 1217 | break; 1218 | } 1219 | include.push( stack[ i ] ); 1220 | } 1221 | if ( include.length ) { 1222 | return include.join( "\n" ); 1223 | } 1224 | } 1225 | return stack[ offset ]; 1226 | } else if ( e.sourceURL ) { 1227 | // Safari, PhantomJS 1228 | // hopefully one day Safari provides actual stacktraces 1229 | // exclude useless self-reference for generated Error objects 1230 | if ( /qunit.js$/.test( e.sourceURL ) ) { 1231 | return; 1232 | } 1233 | // for actual exceptions, this is useful 1234 | return e.sourceURL + ":" + e.line; 1235 | } 1236 | } 1237 | function sourceFromStacktrace( offset ) { 1238 | try { 1239 | throw new Error(); 1240 | } catch ( e ) { 1241 | return extractStacktrace( e, offset ); 1242 | } 1243 | } 1244 | 1245 | function escapeInnerText( s ) { 1246 | if ( !s ) { 1247 | return ""; 1248 | } 1249 | s = s + ""; 1250 | return s.replace( /[\&<>]/g, function( s ) { 1251 | switch( s ) { 1252 | case "&": return "&"; 1253 | case "<": return "<"; 1254 | case ">": return ">"; 1255 | default: return s; 1256 | } 1257 | }); 1258 | } 1259 | 1260 | function synchronize( callback, last ) { 1261 | config.queue.push( callback ); 1262 | 1263 | if ( config.autorun && !config.blocking ) { 1264 | process( last ); 1265 | } 1266 | } 1267 | 1268 | function process( last ) { 1269 | function next() { 1270 | process( last ); 1271 | } 1272 | var start = new Date().getTime(); 1273 | config.depth = config.depth ? config.depth + 1 : 1; 1274 | 1275 | while ( config.queue.length && !config.blocking ) { 1276 | if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { 1277 | config.queue.shift()(); 1278 | } else { 1279 | window.setTimeout( next, 13 ); 1280 | break; 1281 | } 1282 | } 1283 | config.depth--; 1284 | if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { 1285 | done(); 1286 | } 1287 | } 1288 | 1289 | function saveGlobal() { 1290 | config.pollution = []; 1291 | 1292 | if ( config.noglobals ) { 1293 | for ( var key in window ) { 1294 | // in Opera sometimes DOM element ids show up here, ignore them 1295 | if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) { 1296 | continue; 1297 | } 1298 | config.pollution.push( key ); 1299 | } 1300 | } 1301 | } 1302 | 1303 | function checkPollution( name ) { 1304 | var newGlobals, 1305 | deletedGlobals, 1306 | old = config.pollution; 1307 | 1308 | saveGlobal(); 1309 | 1310 | newGlobals = diff( config.pollution, old ); 1311 | if ( newGlobals.length > 0 ) { 1312 | QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); 1313 | } 1314 | 1315 | deletedGlobals = diff( old, config.pollution ); 1316 | if ( deletedGlobals.length > 0 ) { 1317 | QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); 1318 | } 1319 | } 1320 | 1321 | // returns a new Array with the elements that are in a but not in b 1322 | function diff( a, b ) { 1323 | var i, j, 1324 | result = a.slice(); 1325 | 1326 | for ( i = 0; i < result.length; i++ ) { 1327 | for ( j = 0; j < b.length; j++ ) { 1328 | if ( result[i] === b[j] ) { 1329 | result.splice( i, 1 ); 1330 | i--; 1331 | break; 1332 | } 1333 | } 1334 | } 1335 | return result; 1336 | } 1337 | 1338 | function extend( a, b ) { 1339 | for ( var prop in b ) { 1340 | if ( b[ prop ] === undefined ) { 1341 | delete a[ prop ]; 1342 | 1343 | // Avoid "Member not found" error in IE8 caused by setting window.constructor 1344 | } else if ( prop !== "constructor" || a !== window ) { 1345 | a[ prop ] = b[ prop ]; 1346 | } 1347 | } 1348 | 1349 | return a; 1350 | } 1351 | 1352 | function addEvent( elem, type, fn ) { 1353 | if ( elem.addEventListener ) { 1354 | elem.addEventListener( type, fn, false ); 1355 | } else if ( elem.attachEvent ) { 1356 | elem.attachEvent( "on" + type, fn ); 1357 | } else { 1358 | fn(); 1359 | } 1360 | } 1361 | 1362 | function id( name ) { 1363 | return !!( typeof document !== "undefined" && document && document.getElementById ) && 1364 | document.getElementById( name ); 1365 | } 1366 | 1367 | function registerLoggingCallback( key ) { 1368 | return function( callback ) { 1369 | config[key].push( callback ); 1370 | }; 1371 | } 1372 | 1373 | // Supports deprecated method of completely overwriting logging callbacks 1374 | function runLoggingCallbacks( key, scope, args ) { 1375 | //debugger; 1376 | var i, callbacks; 1377 | if ( QUnit.hasOwnProperty( key ) ) { 1378 | QUnit[ key ].call(scope, args ); 1379 | } else { 1380 | callbacks = config[ key ]; 1381 | for ( i = 0; i < callbacks.length; i++ ) { 1382 | callbacks[ i ].call( scope, args ); 1383 | } 1384 | } 1385 | } 1386 | 1387 | // Test for equality any JavaScript type. 1388 | // Author: Philippe Rathé 1389 | QUnit.equiv = (function() { 1390 | 1391 | // Call the o related callback with the given arguments. 1392 | function bindCallbacks( o, callbacks, args ) { 1393 | var prop = QUnit.objectType( o ); 1394 | if ( prop ) { 1395 | if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { 1396 | return callbacks[ prop ].apply( callbacks, args ); 1397 | } else { 1398 | return callbacks[ prop ]; // or undefined 1399 | } 1400 | } 1401 | } 1402 | 1403 | // the real equiv function 1404 | var innerEquiv, 1405 | // stack to decide between skip/abort functions 1406 | callers = [], 1407 | // stack to avoiding loops from circular referencing 1408 | parents = [], 1409 | 1410 | getProto = Object.getPrototypeOf || function ( obj ) { 1411 | return obj.__proto__; 1412 | }, 1413 | callbacks = (function () { 1414 | 1415 | // for string, boolean, number and null 1416 | function useStrictEquality( b, a ) { 1417 | if ( b instanceof a.constructor || a instanceof b.constructor ) { 1418 | // to catch short annotaion VS 'new' annotation of a 1419 | // declaration 1420 | // e.g. var i = 1; 1421 | // var j = new Number(1); 1422 | return a == b; 1423 | } else { 1424 | return a === b; 1425 | } 1426 | } 1427 | 1428 | return { 1429 | "string": useStrictEquality, 1430 | "boolean": useStrictEquality, 1431 | "number": useStrictEquality, 1432 | "null": useStrictEquality, 1433 | "undefined": useStrictEquality, 1434 | 1435 | "nan": function( b ) { 1436 | return isNaN( b ); 1437 | }, 1438 | 1439 | "date": function( b, a ) { 1440 | return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); 1441 | }, 1442 | 1443 | "regexp": function( b, a ) { 1444 | return QUnit.objectType( b ) === "regexp" && 1445 | // the regex itself 1446 | a.source === b.source && 1447 | // and its modifers 1448 | a.global === b.global && 1449 | // (gmi) ... 1450 | a.ignoreCase === b.ignoreCase && 1451 | a.multiline === b.multiline && 1452 | a.sticky === b.sticky; 1453 | }, 1454 | 1455 | // - skip when the property is a method of an instance (OOP) 1456 | // - abort otherwise, 1457 | // initial === would have catch identical references anyway 1458 | "function": function() { 1459 | var caller = callers[callers.length - 1]; 1460 | return caller !== Object && typeof caller !== "undefined"; 1461 | }, 1462 | 1463 | "array": function( b, a ) { 1464 | var i, j, len, loop; 1465 | 1466 | // b could be an object literal here 1467 | if ( QUnit.objectType( b ) !== "array" ) { 1468 | return false; 1469 | } 1470 | 1471 | len = a.length; 1472 | if ( len !== b.length ) { 1473 | // safe and faster 1474 | return false; 1475 | } 1476 | 1477 | // track reference to avoid circular references 1478 | parents.push( a ); 1479 | for ( i = 0; i < len; i++ ) { 1480 | loop = false; 1481 | for ( j = 0; j < parents.length; j++ ) { 1482 | if ( parents[j] === a[i] ) { 1483 | loop = true;// dont rewalk array 1484 | } 1485 | } 1486 | if ( !loop && !innerEquiv(a[i], b[i]) ) { 1487 | parents.pop(); 1488 | return false; 1489 | } 1490 | } 1491 | parents.pop(); 1492 | return true; 1493 | }, 1494 | 1495 | "object": function( b, a ) { 1496 | var i, j, loop, 1497 | // Default to true 1498 | eq = true, 1499 | aProperties = [], 1500 | bProperties = []; 1501 | 1502 | // comparing constructors is more strict than using 1503 | // instanceof 1504 | if ( a.constructor !== b.constructor ) { 1505 | // Allow objects with no prototype to be equivalent to 1506 | // objects with Object as their constructor. 1507 | if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || 1508 | ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { 1509 | return false; 1510 | } 1511 | } 1512 | 1513 | // stack constructor before traversing properties 1514 | callers.push( a.constructor ); 1515 | // track reference to avoid circular references 1516 | parents.push( a ); 1517 | 1518 | for ( i in a ) { // be strict: don't ensures hasOwnProperty 1519 | // and go deep 1520 | loop = false; 1521 | for ( j = 0; j < parents.length; j++ ) { 1522 | if ( parents[j] === a[i] ) { 1523 | // don't go down the same path twice 1524 | loop = true; 1525 | } 1526 | } 1527 | aProperties.push(i); // collect a's properties 1528 | 1529 | if (!loop && !innerEquiv( a[i], b[i] ) ) { 1530 | eq = false; 1531 | break; 1532 | } 1533 | } 1534 | 1535 | callers.pop(); // unstack, we are done 1536 | parents.pop(); 1537 | 1538 | for ( i in b ) { 1539 | bProperties.push( i ); // collect b's properties 1540 | } 1541 | 1542 | // Ensures identical properties name 1543 | return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); 1544 | } 1545 | }; 1546 | }()); 1547 | 1548 | innerEquiv = function() { // can take multiple arguments 1549 | var args = [].slice.apply( arguments ); 1550 | if ( args.length < 2 ) { 1551 | return true; // end transition 1552 | } 1553 | 1554 | return (function( a, b ) { 1555 | if ( a === b ) { 1556 | return true; // catch the most you can 1557 | } else if ( a === null || b === null || typeof a === "undefined" || 1558 | typeof b === "undefined" || 1559 | QUnit.objectType(a) !== QUnit.objectType(b) ) { 1560 | return false; // don't lose time with error prone cases 1561 | } else { 1562 | return bindCallbacks(a, callbacks, [ b, a ]); 1563 | } 1564 | 1565 | // apply transition with (1..n) arguments 1566 | }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) ); 1567 | }; 1568 | 1569 | return innerEquiv; 1570 | }()); 1571 | 1572 | /** 1573 | * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | 1574 | * http://flesler.blogspot.com Licensed under BSD 1575 | * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 1576 | * 1577 | * @projectDescription Advanced and extensible data dumping for Javascript. 1578 | * @version 1.0.0 1579 | * @author Ariel Flesler 1580 | * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} 1581 | */ 1582 | QUnit.jsDump = (function() { 1583 | function quote( str ) { 1584 | return '"' + str.toString().replace( /"/g, '\\"' ) + '"'; 1585 | } 1586 | function literal( o ) { 1587 | return o + ""; 1588 | } 1589 | function join( pre, arr, post ) { 1590 | var s = jsDump.separator(), 1591 | base = jsDump.indent(), 1592 | inner = jsDump.indent(1); 1593 | if ( arr.join ) { 1594 | arr = arr.join( "," + s + inner ); 1595 | } 1596 | if ( !arr ) { 1597 | return pre + post; 1598 | } 1599 | return [ pre, inner + arr, base + post ].join(s); 1600 | } 1601 | function array( arr, stack ) { 1602 | var i = arr.length, ret = new Array(i); 1603 | this.up(); 1604 | while ( i-- ) { 1605 | ret[i] = this.parse( arr[i] , undefined , stack); 1606 | } 1607 | this.down(); 1608 | return join( "[", ret, "]" ); 1609 | } 1610 | 1611 | var reName = /^function (\w+)/, 1612 | jsDump = { 1613 | parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance 1614 | stack = stack || [ ]; 1615 | var inStack, res, 1616 | parser = this.parsers[ type || this.typeOf(obj) ]; 1617 | 1618 | type = typeof parser; 1619 | inStack = inArray( obj, stack ); 1620 | 1621 | if ( inStack != -1 ) { 1622 | return "recursion(" + (inStack - stack.length) + ")"; 1623 | } 1624 | //else 1625 | if ( type == "function" ) { 1626 | stack.push( obj ); 1627 | res = parser.call( this, obj, stack ); 1628 | stack.pop(); 1629 | return res; 1630 | } 1631 | // else 1632 | return ( type == "string" ) ? parser : this.parsers.error; 1633 | }, 1634 | typeOf: function( obj ) { 1635 | var type; 1636 | if ( obj === null ) { 1637 | type = "null"; 1638 | } else if ( typeof obj === "undefined" ) { 1639 | type = "undefined"; 1640 | } else if ( QUnit.is( "regexp", obj) ) { 1641 | type = "regexp"; 1642 | } else if ( QUnit.is( "date", obj) ) { 1643 | type = "date"; 1644 | } else if ( QUnit.is( "function", obj) ) { 1645 | type = "function"; 1646 | } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { 1647 | type = "window"; 1648 | } else if ( obj.nodeType === 9 ) { 1649 | type = "document"; 1650 | } else if ( obj.nodeType ) { 1651 | type = "node"; 1652 | } else if ( 1653 | // native arrays 1654 | toString.call( obj ) === "[object Array]" || 1655 | // NodeList objects 1656 | ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) 1657 | ) { 1658 | type = "array"; 1659 | } else { 1660 | type = typeof obj; 1661 | } 1662 | return type; 1663 | }, 1664 | separator: function() { 1665 | return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; 1666 | }, 1667 | indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing 1668 | if ( !this.multiline ) { 1669 | return ""; 1670 | } 1671 | var chr = this.indentChar; 1672 | if ( this.HTML ) { 1673 | chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); 1674 | } 1675 | return new Array( this._depth_ + (extra||0) ).join(chr); 1676 | }, 1677 | up: function( a ) { 1678 | this._depth_ += a || 1; 1679 | }, 1680 | down: function( a ) { 1681 | this._depth_ -= a || 1; 1682 | }, 1683 | setParser: function( name, parser ) { 1684 | this.parsers[name] = parser; 1685 | }, 1686 | // The next 3 are exposed so you can use them 1687 | quote: quote, 1688 | literal: literal, 1689 | join: join, 1690 | // 1691 | _depth_: 1, 1692 | // This is the list of parsers, to modify them, use jsDump.setParser 1693 | parsers: { 1694 | window: "[Window]", 1695 | document: "[Document]", 1696 | error: "[ERROR]", //when no parser is found, shouldn"t happen 1697 | unknown: "[Unknown]", 1698 | "null": "null", 1699 | "undefined": "undefined", 1700 | "function": function( fn ) { 1701 | var ret = "function", 1702 | name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE 1703 | 1704 | if ( name ) { 1705 | ret += " " + name; 1706 | } 1707 | ret += "( "; 1708 | 1709 | ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); 1710 | return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); 1711 | }, 1712 | array: array, 1713 | nodelist: array, 1714 | "arguments": array, 1715 | object: function( map, stack ) { 1716 | var ret = [ ], keys, key, val, i; 1717 | QUnit.jsDump.up(); 1718 | if ( Object.keys ) { 1719 | keys = Object.keys( map ); 1720 | } else { 1721 | keys = []; 1722 | for ( key in map ) { 1723 | keys.push( key ); 1724 | } 1725 | } 1726 | keys.sort(); 1727 | for ( i = 0; i < keys.length; i++ ) { 1728 | key = keys[ i ]; 1729 | val = map[ key ]; 1730 | ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); 1731 | } 1732 | QUnit.jsDump.down(); 1733 | return join( "{", ret, "}" ); 1734 | }, 1735 | node: function( node ) { 1736 | var a, val, 1737 | open = QUnit.jsDump.HTML ? "<" : "<", 1738 | close = QUnit.jsDump.HTML ? ">" : ">", 1739 | tag = node.nodeName.toLowerCase(), 1740 | ret = open + tag; 1741 | 1742 | for ( a in QUnit.jsDump.DOMAttrs ) { 1743 | val = node[ QUnit.jsDump.DOMAttrs[a] ]; 1744 | if ( val ) { 1745 | ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" ); 1746 | } 1747 | } 1748 | return ret + close + open + "/" + tag + close; 1749 | }, 1750 | functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function 1751 | var args, 1752 | l = fn.length; 1753 | 1754 | if ( !l ) { 1755 | return ""; 1756 | } 1757 | 1758 | args = new Array(l); 1759 | while ( l-- ) { 1760 | args[l] = String.fromCharCode(97+l);//97 is 'a' 1761 | } 1762 | return " " + args.join( ", " ) + " "; 1763 | }, 1764 | key: quote, //object calls it internally, the key part of an item in a map 1765 | functionCode: "[code]", //function calls it internally, it's the content of the function 1766 | attribute: quote, //node calls it internally, it's an html attribute value 1767 | string: quote, 1768 | date: quote, 1769 | regexp: literal, //regex 1770 | number: literal, 1771 | "boolean": literal 1772 | }, 1773 | DOMAttrs: { 1774 | //attributes to dump from nodes, name=>realName 1775 | id: "id", 1776 | name: "name", 1777 | "class": "className" 1778 | }, 1779 | HTML: false,//if true, entities are escaped ( <, >, \t, space and \n ) 1780 | indentChar: " ",//indentation unit 1781 | multiline: true //if true, items in a collection, are separated by a \n, else just a space. 1782 | }; 1783 | 1784 | return jsDump; 1785 | }()); 1786 | 1787 | // from Sizzle.js 1788 | function getText( elems ) { 1789 | var i, elem, 1790 | ret = ""; 1791 | 1792 | for ( i = 0; elems[i]; i++ ) { 1793 | elem = elems[i]; 1794 | 1795 | // Get the text from text nodes and CDATA nodes 1796 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 1797 | ret += elem.nodeValue; 1798 | 1799 | // Traverse everything else, except comment nodes 1800 | } else if ( elem.nodeType !== 8 ) { 1801 | ret += getText( elem.childNodes ); 1802 | } 1803 | } 1804 | 1805 | return ret; 1806 | } 1807 | 1808 | // from jquery.js 1809 | function inArray( elem, array ) { 1810 | if ( array.indexOf ) { 1811 | return array.indexOf( elem ); 1812 | } 1813 | 1814 | for ( var i = 0, length = array.length; i < length; i++ ) { 1815 | if ( array[ i ] === elem ) { 1816 | return i; 1817 | } 1818 | } 1819 | 1820 | return -1; 1821 | } 1822 | 1823 | /* 1824 | * Javascript Diff Algorithm 1825 | * By John Resig (http://ejohn.org/) 1826 | * Modified by Chu Alan "sprite" 1827 | * 1828 | * Released under the MIT license. 1829 | * 1830 | * More Info: 1831 | * http://ejohn.org/projects/javascript-diff-algorithm/ 1832 | * 1833 | * Usage: QUnit.diff(expected, actual) 1834 | * 1835 | * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" 1836 | */ 1837 | QUnit.diff = (function() { 1838 | function diff( o, n ) { 1839 | var i, 1840 | ns = {}, 1841 | os = {}; 1842 | 1843 | for ( i = 0; i < n.length; i++ ) { 1844 | if ( ns[ n[i] ] == null ) { 1845 | ns[ n[i] ] = { 1846 | rows: [], 1847 | o: null 1848 | }; 1849 | } 1850 | ns[ n[i] ].rows.push( i ); 1851 | } 1852 | 1853 | for ( i = 0; i < o.length; i++ ) { 1854 | if ( os[ o[i] ] == null ) { 1855 | os[ o[i] ] = { 1856 | rows: [], 1857 | n: null 1858 | }; 1859 | } 1860 | os[ o[i] ].rows.push( i ); 1861 | } 1862 | 1863 | for ( i in ns ) { 1864 | if ( !hasOwn.call( ns, i ) ) { 1865 | continue; 1866 | } 1867 | if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) { 1868 | n[ ns[i].rows[0] ] = { 1869 | text: n[ ns[i].rows[0] ], 1870 | row: os[i].rows[0] 1871 | }; 1872 | o[ os[i].rows[0] ] = { 1873 | text: o[ os[i].rows[0] ], 1874 | row: ns[i].rows[0] 1875 | }; 1876 | } 1877 | } 1878 | 1879 | for ( i = 0; i < n.length - 1; i++ ) { 1880 | if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && 1881 | n[ i + 1 ] == o[ n[i].row + 1 ] ) { 1882 | 1883 | n[ i + 1 ] = { 1884 | text: n[ i + 1 ], 1885 | row: n[i].row + 1 1886 | }; 1887 | o[ n[i].row + 1 ] = { 1888 | text: o[ n[i].row + 1 ], 1889 | row: i + 1 1890 | }; 1891 | } 1892 | } 1893 | 1894 | for ( i = n.length - 1; i > 0; i-- ) { 1895 | if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && 1896 | n[ i - 1 ] == o[ n[i].row - 1 ]) { 1897 | 1898 | n[ i - 1 ] = { 1899 | text: n[ i - 1 ], 1900 | row: n[i].row - 1 1901 | }; 1902 | o[ n[i].row - 1 ] = { 1903 | text: o[ n[i].row - 1 ], 1904 | row: i - 1 1905 | }; 1906 | } 1907 | } 1908 | 1909 | return { 1910 | o: o, 1911 | n: n 1912 | }; 1913 | } 1914 | 1915 | return function( o, n ) { 1916 | o = o.replace( /\s+$/, "" ); 1917 | n = n.replace( /\s+$/, "" ); 1918 | 1919 | var i, pre, 1920 | str = "", 1921 | out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), 1922 | oSpace = o.match(/\s+/g), 1923 | nSpace = n.match(/\s+/g); 1924 | 1925 | if ( oSpace == null ) { 1926 | oSpace = [ " " ]; 1927 | } 1928 | else { 1929 | oSpace.push( " " ); 1930 | } 1931 | 1932 | if ( nSpace == null ) { 1933 | nSpace = [ " " ]; 1934 | } 1935 | else { 1936 | nSpace.push( " " ); 1937 | } 1938 | 1939 | if ( out.n.length === 0 ) { 1940 | for ( i = 0; i < out.o.length; i++ ) { 1941 | str += "" + out.o[i] + oSpace[i] + ""; 1942 | } 1943 | } 1944 | else { 1945 | if ( out.n[0].text == null ) { 1946 | for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { 1947 | str += "" + out.o[n] + oSpace[n] + ""; 1948 | } 1949 | } 1950 | 1951 | for ( i = 0; i < out.n.length; i++ ) { 1952 | if (out.n[i].text == null) { 1953 | str += "" + out.n[i] + nSpace[i] + ""; 1954 | } 1955 | else { 1956 | // `pre` initialized at top of scope 1957 | pre = ""; 1958 | 1959 | for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { 1960 | pre += "" + out.o[n] + oSpace[n] + ""; 1961 | } 1962 | str += " " + out.n[i].text + nSpace[i] + pre; 1963 | } 1964 | } 1965 | } 1966 | 1967 | return str; 1968 | }; 1969 | }()); 1970 | 1971 | // for CommonJS enviroments, export everything 1972 | if ( typeof exports !== "undefined" ) { 1973 | extend(exports, QUnit); 1974 | } 1975 | 1976 | // get at whatever the global object is, like window in browsers 1977 | }( (function() {return this;}.call()) )); 1978 | -------------------------------------------------------------------------------- /libs/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS 2.0.4 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | var requirejs,require,define; 7 | (function(Y){function x(b){return J.call(b)==="[object Function]"}function G(b){return J.call(b)==="[object Array]"}function q(b,c){if(b){var e;for(e=0;e-1;e-=1)if(b[e]&&c(b[e],e,b))break}}function y(b,c){for(var e in b)if(b.hasOwnProperty(e)&&c(b[e],e))break}function K(b,c,e,i){c&&y(c,function(c,j){if(e||!b.hasOwnProperty(j))i&&typeof c!=="string"?(b[j]||(b[j]={}),K(b[j],c,e,i)):b[j]=c});return b}function s(b, 8 | c){return function(){return c.apply(b,arguments)}}function Z(b){if(!b)return b;var c=Y;q(b.split("."),function(b){c=c[b]});return c}function $(b,c,e){return function(){var i=fa.call(arguments,0),g;if(e&&x(g=i[i.length-1]))g.__requireJsBuild=!0;i.push(c);return b.apply(null,i)}}function aa(b,c,e){q([["toUrl"],["undef"],["defined","requireDefined"],["specified","requireSpecified"]],function(i){var g=i[1]||i[0];b[i[0]]=c?$(c[g],e):function(){var b=z[O];return b[g].apply(b,arguments)}})}function H(b, 9 | c,e,i){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=i;if(e)c.originalError=e;return c}function ga(){if(I&&I.readyState==="interactive")return I;N(document.getElementsByTagName("script"),function(b){if(b.readyState==="interactive")return I=b});return I}var ha=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ia=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,ba=/\.js$/,ja=/^\.\//,J=Object.prototype.toString,A=Array.prototype,fa=A.slice,ka=A.splice,w=!!(typeof window!== 10 | "undefined"&&navigator&&document),ca=!w&&typeof importScripts!=="undefined",la=w&&navigator.platform==="PLAYSTATION 3"?/^complete$/:/^(complete|loaded)$/,O="_",S=typeof opera!=="undefined"&&opera.toString()==="[object Opera]",z={},p={},P=[],L=!1,j,t,C,u,D,I,E,da,ea;if(typeof define==="undefined"){if(typeof requirejs!=="undefined"){if(x(requirejs))return;p=requirejs;requirejs=void 0}typeof require!=="undefined"&&!x(require)&&(p=require,require=void 0);j=requirejs=function(b,c,e,i){var g=O,r;!G(b)&& 11 | typeof b!=="string"&&(r=b,G(c)?(b=c,c=e,e=i):b=[]);if(r&&r.context)g=r.context;(i=z[g])||(i=z[g]=j.s.newContext(g));r&&i.configure(r);return i.require(b,c,e)};j.config=function(b){return j(b)};require||(require=j);j.version="2.0.4";j.jsExtRegExp=/^\/|:|\?|\.js$/;j.isBrowser=w;A=j.s={contexts:z,newContext:function(b){function c(a,d,o){var l=d&&d.split("/"),f=l,b=k.map,c=b&&b["*"],e,g,h;if(a&&a.charAt(0)===".")if(d){f=k.pkgs[d]?l=[d]:l.slice(0,l.length-1);d=a=f.concat(a.split("/"));for(f=0;d[f];f+= 12 | 1)if(e=d[f],e===".")d.splice(f,1),f-=1;else if(e==="..")if(f===1&&(d[2]===".."||d[0]===".."))break;else f>0&&(d.splice(f-1,2),f-=2);f=k.pkgs[d=a[0]];a=a.join("/");f&&a===d+"/"+f.main&&(a=d)}else a.indexOf("./")===0&&(a=a.substring(2));if(o&&(l||c)&&b){d=a.split("/");for(f=d.length;f>0;f-=1){g=d.slice(0,f).join("/");if(l)for(e=l.length;e>0;e-=1)if(o=b[l.slice(0,e).join("/")])if(o=o[g]){h=o;break}!h&&c&&c[g]&&(h=c[g]);if(h){d.splice(0,f,h);a=d.join("/");break}}}return a}function e(a){w&&q(document.getElementsByTagName("script"), 13 | function(d){if(d.getAttribute("data-requiremodule")===a&&d.getAttribute("data-requirecontext")===h.contextName)return d.parentNode.removeChild(d),!0})}function i(a){var d=k.paths[a];if(d&&G(d)&&d.length>1)return e(a),d.shift(),h.undef(a),h.require([a]),!0}function g(a,d,o,b){var f=a?a.indexOf("!"):-1,v=null,e=d?d.name:null,g=a,i=!0,j="",k,m;a||(i=!1,a="_@r"+(N+=1));f!==-1&&(v=a.substring(0,f),a=a.substring(f+1,a.length));v&&(v=c(v,e,b),m=n[v]);a&&(v?j=m&&m.normalize?m.normalize(a,function(a){return c(a, 14 | e,b)}):c(a,e,b):(j=c(a,e,b),k=h.nameToUrl(j)));a=v&&!m&&!o?"_unnormalized"+(O+=1):"";return{prefix:v,name:j,parentMap:d,unnormalized:!!a,url:k,originalName:g,isDefine:i,id:(v?v+"!"+j:j)+a}}function r(a){var d=a.id,o=m[d];o||(o=m[d]=new h.Module(a));return o}function p(a,d,o){var b=a.id,f=m[b];if(n.hasOwnProperty(b)&&(!f||f.defineEmitComplete))d==="defined"&&o(n[b]);else r(a).on(d,o)}function B(a,d){var b=a.requireModules,l=!1;if(d)d(a);else if(q(b,function(d){if(d=m[d])d.error=a,d.events.error&&(l= 15 | !0,d.emit("error",a))}),!l)j.onError(a)}function u(){P.length&&(ka.apply(F,[F.length-1,0].concat(P)),P=[])}function t(a,d,b){a=a&&a.map;d=$(b||h.require,a,d);aa(d,h,a);d.isBrowser=w;return d}function z(a){delete m[a];q(M,function(d,b){if(d.map.id===a)return M.splice(b,1),d.defined||(h.waitCount-=1),!0})}function A(a,d){var b=a.map.id,l=a.depMaps,f;if(a.inited){if(d[b])return a;d[b]=!0;q(l,function(a){if(a=m[a.id])return!a.inited||!a.enabled?(f=null,delete d[b],!0):f=A(a,K({},d))});return f}}function C(a, 16 | d,b){var l=a.map.id,f=a.depMaps;if(a.inited&&a.map.isDefine){if(d[l])return n[l];d[l]=a;q(f,function(f){var f=f.id,c=m[f];!Q[f]&&c&&(!c.inited||!c.enabled?b[l]=!0:(c=C(c,d,b),b[f]||a.defineDepById(f,c)))});a.check(!0);return n[l]}}function D(a){a.check()}function E(){var a=k.waitSeconds*1E3,d=a&&h.startTime+a<(new Date).getTime(),b=[],l=!1,f=!0,c,g,j;if(!T){T=!0;y(m,function(a){c=a.map;g=c.id;if(a.enabled&&!a.error)if(!a.inited&&d)i(g)?l=j=!0:(b.push(g),e(g));else if(!a.inited&&a.fetched&&c.isDefine&& 17 | (l=!0,!c.prefix))return f=!1});if(d&&b.length)return a=H("timeout","Load timeout for modules: "+b,null,b),a.contextName=h.contextName,B(a);f&&(q(M,function(a){if(!a.defined){var a=A(a,{}),d={};a&&(C(a,d,{}),y(d,D))}}),y(m,D));if((!d||j)&&l)if((w||ca)&&!U)U=setTimeout(function(){U=0;E()},50);T=!1}}function V(a){r(g(a[0],null,!0)).init(a[1],a[2])}function J(a){var a=a.currentTarget||a.srcElement,d=h.onScriptLoad;a.detachEvent&&!S?a.detachEvent("onreadystatechange",d):a.removeEventListener("load",d, 18 | !1);d=h.onScriptError;a.detachEvent&&!S||a.removeEventListener("error",d,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}var k={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},shim:{}},m={},W={},F=[],n={},R={},N=1,O=1,M=[],T,X,h,Q,U;Q={require:function(a){return t(a)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports=n[a.map.id]={}},module:function(a){return a.module={id:a.map.id,uri:a.map.url,config:function(){return k.config&&k.config[a.map.id]||{}},exports:n[a.map.id]}}}; 19 | X=function(a){this.events=W[a.id]||{};this.map=a;this.shim=k.shim[a.id];this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};X.prototype={init:function(a,d,b,l){l=l||{};if(!this.inited){this.factory=d;if(b)this.on("error",b);else this.events.error&&(b=s(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.depMaps.rjsSkipMap=a.rjsSkipMap;this.errback=b;this.inited=!0;this.ignore=l.ignore;l.enabled||this.enabled?this.enable():this.check()}},defineDepById:function(a, 20 | d){var b;q(this.depMaps,function(d,f){if(d.id===a)return b=f,!0});return this.defineDep(b,d)},defineDep:function(a,d){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=d)},fetch:function(){if(!this.fetched){this.fetched=!0;h.startTime=(new Date).getTime();var a=this.map;if(this.shim)t(this,!0)(this.shim.deps||[],s(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=this.map.url;R[a]|| 21 | (R[a]=!0,h.load(this.map.id,a))},check:function(a){if(this.enabled&&!this.enabling){var d=this.map.id,b=this.depExports,c=this.exports,f=this.factory,e;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(this.depCount<1&&!this.defined){if(x(f)){if(this.events.error)try{c=h.execCb(d,f,b,c)}catch(g){e=g}else c=h.execCb(d,f,b,c);if(this.map.isDefine)if((b=this.module)&&b.exports!==void 0&&b.exports!==this.exports)c=b.exports;else if(c===void 0&&this.usingExports)c= 22 | this.exports;if(e)return e.requireMap=this.map,e.requireModules=[this.map.id],e.requireType="define",B(this.error=e)}else c=f;this.exports=c;if(this.map.isDefine&&!this.ignore&&(n[d]=c,j.onResourceLoad))j.onResourceLoad(h,this.map,this.depMaps);delete m[d];this.defined=!0;h.waitCount-=1;h.waitCount===0&&(M=[])}this.defining=!1;if(!a&&this.defined&&!this.defineEmitted)this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0}}else this.fetch()}},callPlugin:function(){var a= 23 | this.map,d=a.id,b=g(a.prefix,null,!1,!0);p(b,"defined",s(this,function(b){var f=this.map.name,e=this.map.parentMap?this.map.parentMap.name:null;if(this.map.unnormalized){if(b.normalize&&(f=b.normalize(f,function(a){return c(a,e,!0)})||""),b=g(a.prefix+"!"+f,this.map.parentMap,!1,!0),p(b,"defined",s(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),b=m[b.id]){if(this.events.error)b.on("error",s(this,function(a){this.emit("error",a)}));b.enable()}}else f=s(this,function(a){this.init([], 24 | function(){return a},null,{enabled:!0})}),f.error=s(this,function(a){this.inited=!0;this.error=a;a.requireModules=[d];y(m,function(a){a.map.id.indexOf(d+"_unnormalized")===0&&z(a.map.id)});B(a)}),f.fromText=function(a,d){var b=L;b&&(L=!1);r(g(a));j.exec(d);b&&(L=!0);h.completeLoad(a)},b.load(a.name,t(a.parentMap,!0,function(a,d){a.rjsSkipMap=!0;return h.require(a,d)}),f,k)}));h.enable(b,this);this.pluginMaps[b.id]=b},enable:function(){this.enabled=!0;if(!this.waitPushed)M.push(this),h.waitCount+= 25 | 1,this.waitPushed=!0;this.enabling=!0;q(this.depMaps,s(this,function(a,d){var b,c;if(typeof a==="string"){a=g(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.depMaps.rjsSkipMap);this.depMaps[d]=a;if(b=Q[a.id]){this.depExports[d]=b(this);return}this.depCount+=1;p(a,"defined",s(this,function(a){this.defineDep(d,a);this.check()}));this.errback&&p(a,"error",this.errback)}b=a.id;c=m[b];!Q[b]&&c&&!c.enabled&&h.enable(a,this)}));y(this.pluginMaps,s(this,function(a){var b=m[a.id];b&&!b.enabled&& 26 | h.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){q(this.events[a],function(a){a(b)});a==="error"&&delete this.events[a]}};return h={config:k,contextName:b,registry:m,defined:n,urlFetched:R,waitCount:0,defQueue:F,Module:X,makeModuleMap:g,configure:function(a){a.baseUrl&&a.baseUrl.charAt(a.baseUrl.length-1)!=="/"&&(a.baseUrl+="/");var b=k.pkgs,c=k.shim,e=k.paths,f=k.map;K(k,a,!0);k.paths=K(e,a.paths,!0);if(a.map)k.map= 27 | K(f||{},a.map,!0,!0);if(a.shim)y(a.shim,function(a,b){G(a)&&(a={deps:a});if(a.exports&&!a.exports.__buildReady)a.exports=h.makeShimExports(a.exports);c[b]=a}),k.shim=c;if(a.packages)q(a.packages,function(a){a=typeof a==="string"?{name:a}:a;b[a.name]={name:a.name,location:a.location||a.name,main:(a.main||"main").replace(ja,"").replace(ba,"")}}),k.pkgs=b;y(m,function(a,b){a.map=g(b)});if(a.deps||a.callback)h.require(a.deps||[],a.callback)},makeShimExports:function(a){var b;return typeof a==="string"? 28 | (b=function(){return Z(a)},b.exports=a,b):function(){return a.apply(Y,arguments)}},requireDefined:function(a,b){var c=g(a,b,!1,!0).id;return n.hasOwnProperty(c)},requireSpecified:function(a,b){a=g(a,b,!1,!0).id;return n.hasOwnProperty(a)||m.hasOwnProperty(a)},require:function(a,d,c,e){var f;if(typeof a==="string"){if(x(d))return B(H("requireargs","Invalid require call"),c);if(j.get)return j.get(h,a,d);a=g(a,d,!1,!0);a=a.id;return!n.hasOwnProperty(a)?B(H("notloaded",'Module name "'+a+'" has not been loaded yet for context: '+ 29 | b)):n[a]}c&&!x(c)&&(e=c,c=void 0);d&&!x(d)&&(e=d,d=void 0);for(u();F.length;)if(f=F.shift(),f[0]===null)return B(H("mismatch","Mismatched anonymous define() module: "+f[f.length-1]));else V(f);r(g(null,e)).init(a,d,c,{enabled:!0});E();return h.require},undef:function(a){var b=g(a,null,!0),c=m[a];delete n[a];delete R[b.url];delete W[a];if(c){if(c.events.defined)W[a]=c.events;z(a)}},enable:function(a){m[a.id]&&r(a).enable()},completeLoad:function(a){var b=k.shim[a]||{},c=b.exports&&b.exports.exports, 30 | e,f;for(u();F.length;){f=F.shift();if(f[0]===null){f[0]=a;if(e)break;e=!0}else f[0]===a&&(e=!0);V(f)}f=m[a];if(!e&&!n[a]&&f&&!f.inited)if(k.enforceDefine&&(!c||!Z(c)))if(i(a))return;else return B(H("nodefine","No define call for "+a,null,[a]));else V([a,b.deps||[],b.exports]);E()},toUrl:function(a,b){var e=a.lastIndexOf("."),g=null;e!==-1&&(g=a.substring(e,a.length),a=a.substring(0,e));return h.nameToUrl(c(a,b&&b.id,!0),g)},nameToUrl:function(a,b){var c,e,f,g,h,i;if(j.jsExtRegExp.test(a))g=a+(b|| 31 | "");else{c=k.paths;e=k.pkgs;g=a.split("/");for(h=g.length;h>0;h-=1)if(i=g.slice(0,h).join("/"),f=e[i],i=c[i]){G(i)&&(i=i[0]);g.splice(0,h,i);break}else if(f){c=a===f.name?f.location+"/"+f.main:f.location;g.splice(0,h,c);break}g=g.join("/")+(b||".js");g=(g.charAt(0)==="/"||g.match(/^[\w\+\.\-]+:/)?"":k.baseUrl)+g}return k.urlArgs?g+((g.indexOf("?")===-1?"?":"&")+k.urlArgs):g},load:function(a,b){j.load(h,a,b)},execCb:function(a,b,c,e){return b.apply(e,c)},onScriptLoad:function(a){if(a.type==="load"|| 32 | la.test((a.currentTarget||a.srcElement).readyState))I=null,a=J(a),h.completeLoad(a.id)},onScriptError:function(a){var b=J(a);if(!i(b.id))return B(H("scripterror","Script error",a,[b.id]))}}}};j({});aa(j);if(w&&(t=A.head=document.getElementsByTagName("head")[0],C=document.getElementsByTagName("base")[0]))t=A.head=C.parentNode;j.onError=function(b){throw b;};j.load=function(b,c,e){var i=b&&b.config||{},g;if(w)return g=i.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"), 33 | g.type=i.scriptType||"text/javascript",g.charset="utf-8",g.async=!0,g.setAttribute("data-requirecontext",b.contextName),g.setAttribute("data-requiremodule",c),g.attachEvent&&!(g.attachEvent.toString&&g.attachEvent.toString().indexOf("[native code")<0)&&!S?(L=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)):(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=e,E=g,C?t.insertBefore(g,C):t.appendChild(g),E=null,g;else ca&&(importScripts(e),b.completeLoad(c))}; 34 | w&&N(document.getElementsByTagName("script"),function(b){if(!t)t=b.parentNode;if(u=b.getAttribute("data-main")){if(!p.baseUrl)D=u.split("/"),da=D.pop(),ea=D.length?D.join("/")+"/":"./",p.baseUrl=ea,u=da;u=u.replace(ba,"");p.deps=p.deps?p.deps.concat(u):[u];return!0}});define=function(b,c,e){var i,g;typeof b!=="string"&&(e=c,c=b,b=null);G(c)||(e=c,c=[]);!c.length&&x(e)&&e.length&&(e.toString().replace(ha,"").replace(ia,function(b,e){c.push(e)}),c=(e.length===1?["require"]:["require","exports","module"]).concat(c)); 35 | if(L&&(i=E||ga()))b||(b=i.getAttribute("data-requiremodule")),g=z[i.getAttribute("data-requirecontext")];(g?g.defQueue:P).push([b,c,e])};define.amd={jQuery:!0};j.exec=function(b){return eval(b)};j(p)}})(this); 36 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 2 | requirejs.config({ 3 | paths: { 4 | jQuery: 'libs/jquery', 5 | Handlebars: 'libs/handlebars-1.0.8', 6 | HandlebarsHelpers: 'ddl_builder/handlebarsHelpers', 7 | DateFormat: 'libs/date.format', 8 | DDLBuilder: 'ddl_builder' 9 | }, 10 | 11 | shim: { 12 | jQuery: { 13 | exports: '$' 14 | }, 15 | Handlebars: { 16 | exports: 'Handlebars' 17 | }, 18 | DateFormat: { 19 | exports: 'dateFormat' 20 | } 21 | } 22 | 23 | }); 24 | 25 | require(["jQuery", "DDLBuilder/ddl_builder"], function ($, ddl_builder) { 26 | 27 | $("#parseBtn").on('click', function () { 28 | var dbType = $("#dbType").val(), 29 | terminator = $("#terminator").val(), 30 | builder = new ddl_builder(); 31 | 32 | 33 | builder.setupForDBType(dbType,terminator); 34 | 35 | $("#output").val( 36 | builder.parse( $("#input").val() ) 37 | ); 38 | 39 | }); 40 | }); -------------------------------------------------------------------------------- /nodetest.js: -------------------------------------------------------------------------------- 1 | var jsdom = require("jsdom"), 2 | fs = require("fs"), 3 | requirejs = require("requirejs"); 4 | 5 | 6 | fs.readFile("qunit.html","utf8", function (err, data) { 7 | jsdom.env(data, [], function(errors, window) { 8 | 9 | //glabals needed for QUnit 10 | (function() { 11 | this.addEventListener = window.addEventListener; 12 | this.location = this.location || {}; 13 | this.location.pathname= ""; 14 | this.location.search = ""; 15 | }.call()); 16 | document = window.document; 17 | navigator = {userAgent: "nodejs"}; 18 | 19 | requirejs.config({ 20 | paths: { 21 | Handlebars: 'libs/handlebars-1.0.8', 22 | HandlebarsHelpers: 'ddl_builder/handlebarsHelpers', 23 | DateFormat: 'libs/date.format', 24 | DDLBuilder: 'ddl_builder', 25 | QUnit: 'libs/qunit-1.10.0' 26 | }, 27 | 28 | shim: { 29 | Handlebars: { 30 | exports: 'Handlebars' 31 | }, 32 | DateFormat: { 33 | exports: "dateFormat" 34 | }, 35 | QUnit: { 36 | exports: function () { 37 | return { "test": test, "equal": equal, "ok": ok, 38 | "load": QUnit.load, "done": QUnit.done }; 39 | } 40 | } 41 | } 42 | 43 | }); 44 | 45 | // silly hack to push the proper jQuery reference into the requirejs cache 46 | requirejs.define("jQuery", function () { 47 | return require("jquery").create(window); 48 | }); 49 | 50 | requirejs(["jQuery", "QUnit", "DDLBuilder/qunit/main"], function ($, QUnit) { 51 | QUnit.load(); 52 | QUnit.done(function (details) { 53 | console.log(details); 54 | if ($("#qunit-tests li.fail").length) { 55 | console.log($("#qunit-tests li.fail").html()); 56 | } 57 | 58 | process.exit(details.failed); 59 | }); 60 | }); 61 | }); 62 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DDLBuilder", 3 | "version": "1.0.0", 4 | "devDependencies": { 5 | "requirejs": "2.0.6", 6 | "jsdom": ">=0.3.4", 7 | "jquery": "1.8.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | QUnit Tests for DDL Builder 5 | 6 | 7 | 8 |
    9 |
    10 | 11 | 12 | -------------------------------------------------------------------------------- /qunit_main.js: -------------------------------------------------------------------------------- 1 | 2 | requirejs.config({ 3 | paths: { 4 | jQuery: 'libs/jquery', 5 | Handlebars: 'libs/handlebars-1.0.8', 6 | HandlebarsHelpers: 'ddl_builder/handlebarsHelpers', 7 | DateFormat: 'libs/date.format', 8 | DDLBuilder: 'ddl_builder', 9 | QUnit: 'libs/qunit-1.10.0' 10 | }, 11 | 12 | shim: { 13 | jQuery: { 14 | exports: '$' 15 | }, 16 | Handlebars: { 17 | exports: 'Handlebars' 18 | }, 19 | DateFormat: { 20 | exports: 'dateFormat' 21 | }, 22 | QUnit: { 23 | exports: function () { return { "test": test, "equal": equal, "ok": ok }; } 24 | } 25 | } 26 | 27 | }); 28 | 29 | require(["jQuery", "DDLBuilder/qunit/main"], function () {}); -------------------------------------------------------------------------------- /run-qunit.js: -------------------------------------------------------------------------------- 1 | var system = require('system'); 2 | 3 | /** 4 | * Wait until the test condition is true or a timeout occurs. Useful for waiting 5 | * on a server response or for a ui change (fadeIn, etc.) to occur. 6 | * 7 | * @param testFx javascript condition that evaluates to a boolean, 8 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or 9 | * as a callback function. 10 | * @param onReady what to do when testFx condition is fulfilled, 11 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or 12 | * as a callback function. 13 | * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. 14 | */ 15 | function waitFor(testFx, onReady, timeOutMillis) { 16 | var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s 17 | start = new Date().getTime(), 18 | condition = false, 19 | interval = setInterval(function() { 20 | if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { 21 | // If not time-out yet and condition not yet fulfilled 22 | condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code 23 | } else { 24 | if(!condition) { 25 | // If condition still not fulfilled (timeout but condition is 'false') 26 | console.log("'waitFor()' timeout"); 27 | phantom.exit(1); 28 | } else { 29 | // Condition fulfilled (timeout and/or condition is 'true') 30 | console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); 31 | typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled 32 | clearInterval(interval); //< Stop this interval 33 | } 34 | } 35 | }, 100); //< repeat check every 250ms 36 | }; 37 | 38 | 39 | if (system.args.length !== 2) { 40 | console.log('Usage: run-qunit.js URL'); 41 | phantom.exit(1); 42 | } 43 | 44 | var page = require('webpage').create(); 45 | 46 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") 47 | page.onConsoleMessage = function(msg) { 48 | console.log(msg); 49 | }; 50 | 51 | page.open(system.args[1], function(status){ 52 | if (status !== "success") { 53 | console.log("Unable to access network"); 54 | phantom.exit(1); 55 | } else { 56 | waitFor(function(){ 57 | return page.evaluate(function(){ 58 | var el = document.getElementById('qunit-testresult'); 59 | if (el && el.innerText.match('completed')) { 60 | return true; 61 | } 62 | return false; 63 | }); 64 | }, function(){ 65 | var failedNum = page.evaluate(function(){ 66 | var el = document.getElementById('qunit-testresult'); 67 | console.log(el.innerText); 68 | try { 69 | var tests = document.getElementById('qunit-tests').children; 70 | for (var i = 0; i 0) ? 1 : 0); 90 | }); 91 | } 92 | }); 93 | -------------------------------------------------------------------------------- /text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license RequireJS text 2.0.3 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/requirejs/text for details 5 | */ 6 | /*jslint regexp: true */ 7 | /*global require: false, XMLHttpRequest: false, ActiveXObject: false, 8 | define: false, window: false, process: false, Packages: false, 9 | java: false, location: false */ 10 | 11 | define(['module'], function (module) { 12 | 'use strict'; 13 | 14 | var text, fs, 15 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 16 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, 17 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 18 | hasLocation = typeof location !== 'undefined' && location.href, 19 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 20 | defaultHostName = hasLocation && location.hostname, 21 | defaultPort = hasLocation && (location.port || undefined), 22 | buildMap = [], 23 | masterConfig = (module.config && module.config()) || {}; 24 | 25 | text = { 26 | version: '2.0.3', 27 | 28 | strip: function (content) { 29 | //Strips declarations so that external SVG and XML 30 | //documents can be added to a document without worry. Also, if the string 31 | //is an HTML document, only the part inside the body tag is returned. 32 | if (content) { 33 | content = content.replace(xmlRegExp, ""); 34 | var matches = content.match(bodyRegExp); 35 | if (matches) { 36 | content = matches[1]; 37 | } 38 | } else { 39 | content = ""; 40 | } 41 | return content; 42 | }, 43 | 44 | jsEscape: function (content) { 45 | return content.replace(/(['\\])/g, '\\$1') 46 | .replace(/[\f]/g, "\\f") 47 | .replace(/[\b]/g, "\\b") 48 | .replace(/[\n]/g, "\\n") 49 | .replace(/[\t]/g, "\\t") 50 | .replace(/[\r]/g, "\\r") 51 | .replace(/[\u2028]/g, "\\u2028") 52 | .replace(/[\u2029]/g, "\\u2029"); 53 | }, 54 | 55 | createXhr: masterConfig.createXhr || function () { 56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 57 | var xhr, i, progId; 58 | if (typeof XMLHttpRequest !== "undefined") { 59 | return new XMLHttpRequest(); 60 | } else if (typeof ActiveXObject !== "undefined") { 61 | for (i = 0; i < 3; i += 1) { 62 | progId = progIds[i]; 63 | try { 64 | xhr = new ActiveXObject(progId); 65 | } catch (e) {} 66 | 67 | if (xhr) { 68 | progIds = [progId]; // so faster next time 69 | break; 70 | } 71 | } 72 | } 73 | 74 | return xhr; 75 | }, 76 | 77 | /** 78 | * Parses a resource name into its component parts. Resource names 79 | * look like: module/name.ext!strip, where the !strip part is 80 | * optional. 81 | * @param {String} name the resource name 82 | * @returns {Object} with properties "moduleName", "ext" and "strip" 83 | * where strip is a boolean. 84 | */ 85 | parseName: function (name) { 86 | var strip = false, index = name.indexOf("."), 87 | modName = name.substring(0, index), 88 | ext = name.substring(index + 1, name.length); 89 | 90 | index = ext.indexOf("!"); 91 | if (index !== -1) { 92 | //Pull off the strip arg. 93 | strip = ext.substring(index + 1, ext.length); 94 | strip = strip === "strip"; 95 | ext = ext.substring(0, index); 96 | } 97 | 98 | return { 99 | moduleName: modName, 100 | ext: ext, 101 | strip: strip 102 | }; 103 | }, 104 | 105 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 106 | 107 | /** 108 | * Is an URL on another domain. Only works for browser use, returns 109 | * false in non-browser environments. Only used to know if an 110 | * optimized .js version of a text resource should be loaded 111 | * instead. 112 | * @param {String} url 113 | * @returns Boolean 114 | */ 115 | useXhr: function (url, protocol, hostname, port) { 116 | var uProtocol, uHostName, uPort, 117 | match = text.xdRegExp.exec(url); 118 | if (!match) { 119 | return true; 120 | } 121 | uProtocol = match[2]; 122 | uHostName = match[3]; 123 | 124 | uHostName = uHostName.split(':'); 125 | uPort = uHostName[1]; 126 | uHostName = uHostName[0]; 127 | 128 | return (!uProtocol || uProtocol === protocol) && 129 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && 130 | ((!uPort && !uHostName) || uPort === port); 131 | }, 132 | 133 | finishLoad: function (name, strip, content, onLoad) { 134 | content = strip ? text.strip(content) : content; 135 | if (masterConfig.isBuild) { 136 | buildMap[name] = content; 137 | } 138 | onLoad(content); 139 | }, 140 | 141 | load: function (name, req, onLoad, config) { 142 | //Name has format: some.module.filext!strip 143 | //The strip part is optional. 144 | //if strip is present, then that means only get the string contents 145 | //inside a body tag in an HTML string. For XML/SVG content it means 146 | //removing the declarations so the content can be inserted 147 | //into the current doc without problems. 148 | 149 | // Do not bother with the work if a build and text will 150 | // not be inlined. 151 | if (config.isBuild && !config.inlineText) { 152 | onLoad(); 153 | return; 154 | } 155 | 156 | masterConfig.isBuild = config.isBuild; 157 | 158 | var parsed = text.parseName(name), 159 | nonStripName = parsed.moduleName + '.' + parsed.ext, 160 | url = req.toUrl(nonStripName), 161 | useXhr = (masterConfig.useXhr) || 162 | text.useXhr; 163 | 164 | //Load the text. Use XHR if possible and in a browser. 165 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 166 | text.get(url, function (content) { 167 | text.finishLoad(name, parsed.strip, content, onLoad); 168 | }, function (err) { 169 | if (onLoad.error) { 170 | onLoad.error(err); 171 | } 172 | }); 173 | } else { 174 | //Need to fetch the resource across domains. Assume 175 | //the resource has been optimized into a JS module. Fetch 176 | //by the module name + extension, but do not include the 177 | //!strip part to avoid file system issues. 178 | req([nonStripName], function (content) { 179 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 180 | parsed.strip, content, onLoad); 181 | }); 182 | } 183 | }, 184 | 185 | write: function (pluginName, moduleName, write, config) { 186 | if (buildMap.hasOwnProperty(moduleName)) { 187 | var content = text.jsEscape(buildMap[moduleName]); 188 | write.asModule(pluginName + "!" + moduleName, 189 | "define(function () { return '" + 190 | content + 191 | "';});\n"); 192 | } 193 | }, 194 | 195 | writeFile: function (pluginName, moduleName, req, write, config) { 196 | var parsed = text.parseName(moduleName), 197 | nonStripName = parsed.moduleName + '.' + parsed.ext, 198 | //Use a '.js' file name so that it indicates it is a 199 | //script that can be loaded across domains. 200 | fileName = req.toUrl(parsed.moduleName + '.' + 201 | parsed.ext) + '.js'; 202 | 203 | //Leverage own load() method to load plugin value, but only 204 | //write out values that do not have the strip argument, 205 | //to avoid any potential issues with ! in file names. 206 | text.load(nonStripName, req, function (value) { 207 | //Use own write() method to construct full module value. 208 | //But need to create shell that translates writeFile's 209 | //write() to the right interface. 210 | var textWrite = function (contents) { 211 | return write(fileName, contents); 212 | }; 213 | textWrite.asModule = function (moduleName, contents) { 214 | return write.asModule(moduleName, fileName, contents); 215 | }; 216 | 217 | text.write(pluginName, nonStripName, textWrite, config); 218 | }, config); 219 | } 220 | }; 221 | 222 | if (masterConfig.env === 'node' || (!masterConfig.env && 223 | typeof process !== "undefined" && 224 | process.versions && 225 | !!process.versions.node)) { 226 | //Using special require.nodeRequire, something added by r.js. 227 | fs = require.nodeRequire('fs'); 228 | 229 | text.get = function (url, callback) { 230 | var file = fs.readFileSync(url, 'utf8'); 231 | //Remove BOM (Byte Mark Order) from utf8 files if it is there. 232 | if (file.indexOf('\uFEFF') === 0) { 233 | file = file.substring(1); 234 | } 235 | callback(file); 236 | }; 237 | } else if (masterConfig.env === 'xhr' || (!masterConfig.env && 238 | text.createXhr())) { 239 | text.get = function (url, callback, errback) { 240 | var xhr = text.createXhr(); 241 | xhr.open('GET', url, true); 242 | 243 | //Allow overrides specified in config 244 | if (masterConfig.onXhr) { 245 | masterConfig.onXhr(xhr, url); 246 | } 247 | 248 | xhr.onreadystatechange = function (evt) { 249 | var status, err; 250 | //Do not explicitly handle errors, those should be 251 | //visible via console output in the browser. 252 | if (xhr.readyState === 4) { 253 | status = xhr.status; 254 | if (status > 399 && status < 600) { 255 | //An http 4xx or 5xx error. Signal an error. 256 | err = new Error(url + ' HTTP status: ' + status); 257 | err.xhr = xhr; 258 | errback(err); 259 | } else { 260 | callback(xhr.responseText); 261 | } 262 | } 263 | }; 264 | xhr.send(null); 265 | }; 266 | } else if (masterConfig.env === 'rhino' || (!masterConfig.env && 267 | typeof Packages !== 'undefined' && typeof java !== 'undefined')) { 268 | //Why Java, why is this so awkward? 269 | text.get = function (url, callback) { 270 | var stringBuffer, line, 271 | encoding = "utf-8", 272 | file = new java.io.File(url), 273 | lineSeparator = java.lang.System.getProperty("line.separator"), 274 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 275 | content = ''; 276 | try { 277 | stringBuffer = new java.lang.StringBuffer(); 278 | line = input.readLine(); 279 | 280 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 281 | // http://www.unicode.org/faq/utf_bom.html 282 | 283 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 284 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 285 | if (line && line.length() && line.charAt(0) === 0xfeff) { 286 | // Eat the BOM, since we've already found the encoding on this file, 287 | // and we plan to concatenating this buffer with others; the BOM should 288 | // only appear at the top of a file. 289 | line = line.substring(1); 290 | } 291 | 292 | stringBuffer.append(line); 293 | 294 | while ((line = input.readLine()) !== null) { 295 | stringBuffer.append(lineSeparator); 296 | stringBuffer.append(line); 297 | } 298 | //Make sure we return a JavaScript string and not a Java string. 299 | content = String(stringBuffer.toString()); //String 300 | } finally { 301 | input.close(); 302 | } 303 | callback(content); 304 | }; 305 | } 306 | 307 | return text; 308 | }); 309 | --------------------------------------------------------------------------------