The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitattributes
├── .gitignore
├── .travis.yml
├── DO_Powered_by_Badge_blue.png
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── build
    ├── blocks-api-parser.js
    ├── init.js
    ├── publish
    │   ├── GetClosedIssuesAndMergedPRs.graphql
    │   ├── GetMasterCommits.graphql
    │   └── github-graphql.js
    └── tasks
    │   ├── bower.js
    │   ├── build-test-definitions.js
    │   ├── build.js
    │   ├── combine.js
    │   ├── debug.js
    │   ├── npm.js
    │   ├── publish.js
    │   └── test.js
├── dist
    ├── blocks-source.js
    ├── blocks.js
    ├── blocks.min.js
    ├── blocks.min.js.map
    ├── mvc
    │   ├── blocks-mvc.js
    │   ├── blocks-mvc.min.js
    │   └── blocks-mvc.min.js.map
    ├── node
    │   └── blocks-node.js
    └── query
    │   ├── blocks-query-data.js
    │   ├── blocks-query-data.min.js
    │   ├── blocks-query-data.min.js.map
    │   ├── blocks-query.js
    │   ├── blocks-query.min.js
    │   └── blocks-query.min.js.map
├── lib
    └── blocks
    │   ├── core.js
    │   ├── jsdebug.js
    │   └── value-nocore.js
├── package.json
├── src
    ├── .jshintrc
    ├── DataSource.js
    ├── core.js
    ├── dataSource
    │   └── DataSource.js
    ├── modules
    │   ├── Escape.js
    │   ├── Event.js
    │   ├── Events.js
    │   ├── Request.js
    │   ├── Router.js
    │   ├── ajax.js
    │   ├── createProperty.js
    │   ├── keys.js
    │   ├── parseCallback.js
    │   └── uniqueId.js
    ├── mvc.js
    ├── mvc
    │   ├── Application.js
    │   ├── Collection.js
    │   ├── History.js
    │   ├── Model.js
    │   ├── Property.js
    │   ├── View.js
    │   ├── bindContext.js
    │   ├── clonePrototype.js
    │   ├── queries.js
    │   ├── validation.js
    │   ├── validators.js
    │   └── var
    │   │   ├── COLLECTION.js
    │   │   └── MODEL.js
    ├── node.js
    ├── node
    │   ├── .jshintrc
    │   ├── BrowserEnv.js
    │   ├── Middleware.js
    │   ├── Server.js
    │   ├── ServerEnv.js
    │   ├── browserVars.js
    │   ├── createBrowserEnvObject.js
    │   ├── executePageScripts.js
    │   ├── findPageScripts.js
    │   ├── getElementsById.js
    │   ├── methods.js
    │   ├── overrides.js
    │   └── parseToVirtual.js
    ├── query.js
    ├── query
    │   ├── ChunkManager.js
    │   ├── DomQuery.js
    │   ├── ElementsData.js
    │   ├── Expression.js
    │   ├── ExtenderHelper.js
    │   ├── Observer.js
    │   ├── VirtualComment.js
    │   ├── VirtualElement.js
    │   ├── addListener.js
    │   ├── animation.js
    │   ├── browser.js
    │   ├── createFragment.js
    │   ├── createVirtual.js
    │   ├── dom.js
    │   ├── extenders.js
    │   ├── getClassIndex.js
    │   ├── methods.js
    │   ├── observable.js
    │   ├── on.js
    │   ├── parseQuery.js
    │   ├── queries.js
    │   ├── ready.js
    │   ├── serverData.js
    │   ├── setClass.js
    │   └── var
    │   │   ├── OBSERVABLE.js
    │   │   ├── classAttr.js
    │   │   ├── dataIdAttr.js
    │   │   ├── dataQueryAttr.js
    │   │   ├── parameterQueryCache.js
    │   │   ├── queries.js
    │   │   └── virtualElementIdentity.js
    └── var
    │   ├── hasOwn.js
    │   ├── identity.js
    │   ├── slice.js
    │   ├── strundefined.js
    │   ├── support.js
    │   ├── toString.js
    │   └── trimRegExp.js
└── test
    ├── .jshintrc
    ├── Runner.html
    ├── blocks.testing.js
    ├── pages
        ├── input.html
        ├── parsing-each.html
        ├── parsing.html
        ├── styles.css
        └── two-way-data-binding.html
    ├── runner-styles.css
    ├── spec
        ├── dataSource
        │   ├── api.js
        │   ├── configuration.js
        │   └── events.js
        ├── mvc
        │   ├── application.js
        │   ├── collection.js
        │   ├── history.js
        │   ├── model.js
        │   ├── property.js
        │   ├── router.js
        │   ├── validation.js
        │   └── view.js
        └── query
        │   ├── attribute-queries.js
        │   ├── contexts.js
        │   ├── custom-queries.js
        │   ├── element.js
        │   ├── expressions.js
        │   ├── extenders.js
        │   ├── html-parsing.js
        │   ├── observable.array.js
        │   ├── observables.comments.js
        │   ├── observables.dom.js
        │   ├── observables.js
        │   ├── public-methods.js
        │   ├── queries.comments.js
        │   └── queries.js
    └── tests.json


/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | 
4 | # JS files must always use LF for tools to work
5 | *.js eol=lf


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | # Folders
 2 | .idea
 3 | node_modules
 4 | /examples
 5 | /dist/npm
 6 | # at least for now exclude the coverage parts
 7 | /coverage
 8 | 
 9 | # Files
10 | .DS_Store
11 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | language: node_js
 2 | node_js:
 3 |   - "6.9.4"
 4 | before_install: npm install -g grunt-cli
 5 | before_script:
 6 |   - grunt compile
 7 |   - export DISPLAY=:99.0
 8 |   - sh -e /etc/init.d/xvfb start
 9 | after_success:
10 |   - grunt publish
11 | addons:
12 |   apt:
13 |     sources:
14 |       - ubuntu-toolchain-r-test
15 |     packages:
16 |       - g++-4.8
17 | 


--------------------------------------------------------------------------------
/DO_Powered_by_Badge_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/astoilkov/jsblocks/d3edd4b8d05960ce8785491d7c6c2bb54aae0b9b/DO_Powered_by_Badge_blue.png


--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |   'use strict';
3 | 
4 |   // init grunt configuration from external file
5 |   require('./build/init')(grunt);
6 | };
7 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright jsblocks(c) 2015
 4 | 
 5 | The following license applies to all parts of this software except as
 6 | documented below:
 7 | 
 8 | ====
 9 | 
10 | Permission is hereby granted, free of charge, to any person obtaining a copy
11 | of this software and associated documentation files (the "Software"), to deal
12 | in the Software without restriction, including without limitation the rights
13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | copies of the Software, and to permit persons to whom the Software is
15 | furnished to do so, subject to the following conditions:
16 | 
17 | The above copyright notice and this permission notice shall be included in all
18 | copies or substantial portions of the Software.
19 | 
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 | 
28 | ====
29 | 
30 | All files located in the node_modules, lib and external directories are
31 | externally maintained libraries used by this software which have their
32 | own licenses; we recommend you read them, as their terms may differ from
33 | the terms above.
34 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | ❗❗❗ I started working on this project in 2012. React didn't exist, Angular didn't have a stable 1.0 release, Internet Explorer 7, 8, and 9 was used by 35% of users worldwide, I was 20 years old. I am proud of what I did then but it was overly ambitious for a single person. The repo is now a showcase of my early skills. More about my current work [here](https://astoilkov.com).
 2 | 
 3 | ---
 4 | 
 5 | [![jsblocks](http://jsblocks.com/img/logoBeta.png)](http://jsblocks.com)
 6 | 
 7 | ### Better MV-ish Framework
 8 | 
 9 | ##### From simple user interfaces to complex single-page applications using faster, server-side rendered and easy to learn framework.
10 | 
11 | [[ official website ]](https://web.archive.org/web/20241002181521/http://jsblocks.com/)
12 | 
13 | ### Features
14 | 
15 |  * [Server-side rendering](https://web.archive.org/web/20231207171618/http://jsblocks.com/learn/introduction-why-jsblocks#server-side-rendering)
16 |  * [Debugging experience](https://web.archive.org/web/20231207171618/http://jsblocks.com/learn/introduction-why-jsblocks#debugging-experience)
17 |  * [Faster](https://web.archive.org/web/20241002181521/http://jsblocks.com/#performance)
18 |  * [MV-ish](https://web.archive.org/web/20231207171618/http://jsblocks.com/learn/introduction-why-jsblocks#mv-ish)
19 |  * [Modular](http://jsblocks.com/learn/introduction-why-jsblocks#modular)
20 |  * [Built-in utility library](https://web.archive.org/web/20231207171618/http://jsblocks.com/learn/introduction-why-jsblocks#built-in-utility-library)
21 |  * [Forward thinking](https://web.archive.org/web/20231207171618/http://jsblocks.com/learn/introduction-why-jsblocks#forward-thinking)
22 |  * [... and many more](https://web.archive.org/web/20231207171618/http://jsblocks.com/learn/introduction-why-jsblocks#feature-rich)
23 | 
24 | ### Example projects
25 |  * [TodoMVC](https://github.com/astoilkov/jsblocks-todomvc)
26 |  * [E-shopping](https://github.com/astoilkov/jsblocks-shopping-example)
27 | 
28 | <br>
29 | 
30 | <img src="./DO_Powered_by_Badge_blue.png" width="300px">
31 | 


--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "name": "blocks",
 3 |     "main": "dist/blocks.js",
 4 |     "license": "MIT",
 5 |     "author": {
 6 |         "name": "Antonio Stoilkov",
 7 |         "email": "antonio.stoilkov@gmail.com",
 8 |         "url": "http://jsblocks.com"
 9 |     },
10 |     "ignore": [
11 |         "build",
12 |         "examples",
13 |         "lib",
14 |         "node_modules",
15 |         "src",
16 |         "test",
17 |         "*.gitignore",
18 |         "Gruntile.js",
19 |         "package.json",
20 |         "dist/npm"
21 |     ],
22 |     "keywords": []
23 | }


--------------------------------------------------------------------------------
/build/init.js:
--------------------------------------------------------------------------------
  1 | module.exports = function (grunt) {
  2 |   'use strict';
  3 | 
  4 |   grunt.loadNpmTasks('grunt-contrib-requirejs');
  5 |   grunt.loadNpmTasks('grunt-contrib-uglify');
  6 |   grunt.loadNpmTasks('grunt-contrib-jshint');
  7 |   grunt.loadNpmTasks('grunt-contrib-watch');
  8 |   grunt.loadNpmTasks('grunt-preprocess');
  9 |   grunt.loadNpmTasks('grunt-karma');
 10 |   grunt.loadNpmTasks('grunt-notify');
 11 | 
 12 |   var pkg = grunt.file.readJSON('package.json')
 13 |   grunt.initConfig({
 14 |     pkg: pkg,
 15 | 
 16 |     version: pkg.version,
 17 | 
 18 |     banner: '/*! jsblocks v<%= pkg.version %> | ' +
 19 |     '(c) 2014, <%= grunt.template.today("yyyy") %> |' +
 20 |     'jsblocks.org/license */',
 21 | 
 22 |     watch: {
 23 |       compile: {
 24 |         files: ['src/**/*.js'],
 25 |         tasks: ['compile'],
 26 |         options: {
 27 |           interrupt: true
 28 |         }
 29 |       }
 30 |     },
 31 | 
 32 |     preprocess: {
 33 |       debug: {
 34 |         src: ['dist/blocks-source.js'],
 35 |         dest: 'dist/blocks.js',
 36 |         options: {
 37 |           context: {
 38 |             DEBUG: true,
 39 |             SERVER: false
 40 |           }
 41 |         }
 42 |       },
 43 | 
 44 |       client: {
 45 |         src: [
 46 |           'dist/blocks-source.js',
 47 |           'dist/mvc/blocks-mvc.js',
 48 |           'dist/query/blocks-query.js',
 49 |           'dist/query/blocks-query-data.js'
 50 |         ],
 51 |         options: {
 52 |           inline: true,
 53 |           context: {
 54 |             DEBUG: false,
 55 |             SERVER: false
 56 |           }
 57 |         }
 58 |       },
 59 | 
 60 |       server: {
 61 |         src: 'dist/node/blocks-node.js',
 62 |         options: {
 63 |           inline: true,
 64 |           context: {
 65 |             DEBUG: false,
 66 |             SERVER: true
 67 |           }
 68 |         }
 69 |       }
 70 |     },
 71 | 
 72 |     uglify: {
 73 |       build: {
 74 |         options: {
 75 |           sourceMap: true
 76 |         },
 77 |         files: {
 78 |           'dist/blocks.min.js': ['dist/blocks-source.js'],
 79 |           'dist/mvc/blocks-mvc.min.js': ['dist/mvc/blocks-mvc.js'],
 80 |           'dist/query/blocks-query.min.js': ['dist/query/blocks-query.js'],
 81 |           'dist/query/blocks-query-data.min.js': ['dist/query/blocks-query-data.js']
 82 |         }
 83 |       }
 84 |     },
 85 | 
 86 |     notify: {
 87 |       build: {
 88 |         options: {
 89 |           message: 'Build successful'
 90 |         }
 91 |       }
 92 |     },
 93 | 
 94 |     jshint: {
 95 |       options: {
 96 |         jshintrc: true
 97 |       },
 98 | 
 99 |       source: ['src/**/*.js']
100 |       //test: ['test/spec/**/*.js'],
101 |       //grunt: ['build/**/*.js']
102 |     }
103 |   });
104 | 
105 |   grunt.loadTasks('build/tasks');
106 | 
107 |   grunt.registerTask('compile', ['build', 'combine', 'preprocess', 'debug', 'build-tests-definitions']);
108 |   grunt.registerTask('live-compile', ['compile', 'watch:compile']);
109 |   grunt.registerTask('full-build', ['jshint', 'compile', 'uglify', 'test', 'npm', 'bower']);
110 |   grunt.registerTask('build-only', ['jshint', 'compile', 'uglify', 'npm', 'bower']);
111 |   grunt.registerTask('default', []);
112 | };
113 | 


--------------------------------------------------------------------------------
/build/publish/GetClosedIssuesAndMergedPRs.graphql:
--------------------------------------------------------------------------------
 1 | query ($owner: String!, $repo: String!, $afterIssues: String, $afterPRs: String, $includePRs: Boolean!, $includeIssues: Boolean!) {
 2 |   repository(owner: $owner, name: $repo) {
 3 |     ...issues @include(if: $includeIssues)
 4 |     ...prs @include(if: $includePRs)
 5 |     parent {
 6 |       ...issues @include(if: $includeIssues)
 7 |       ...prs @include(if: $includePRs)
 8 |     }
 9 |   }
10 | }
11 | 
12 | fragment issues on Repository {
13 |   issues(first: 100, after: $afterIssues, states: [CLOSED]) {
14 |     pageInfo {
15 |       hasNextPage
16 |       endCursor
17 |     }
18 |     nodes {
19 |       number
20 |       url
21 |       title
22 |       timeline(first: 100) {
23 |         nodes {
24 |           ... on ReferencedEvent {
25 |             commit {
26 |               oid
27 |             }
28 |           }
29 |           ... on ClosedEvent {
30 |             closeCommit: commit {
31 |               oid
32 |             }
33 |           }
34 |         }
35 |       }
36 |     }
37 |   }
38 | }
39 | 
40 | fragment prs on Repository {
41 |   pullRequests(first: 100, after: $afterPRs, states: [MERGED]) {
42 |     pageInfo {
43 |       hasNextPage
44 |       endCursor
45 |     }
46 |     nodes {
47 |       title
48 |       url
49 |       number
50 |       timeline(first: 100) {
51 |         nodes {
52 |           ... on MergedEvent {
53 |             commit {
54 |               oid
55 |             }
56 |           }
57 |         }
58 |       }
59 |      # unfortunatly seems to be buggy in the alpha of github graphql
60 |      # sometime "MERGED" PRs have a mergeCommit and sometimes not
61 |      # while they are having a "MergedEvent" timeline
62 |      # mergeCommit {
63 |      #   oid
64 |      # }
65 |     }
66 |   }
67 | }


--------------------------------------------------------------------------------
/build/publish/GetMasterCommits.graphql:
--------------------------------------------------------------------------------
 1 | query ($owner: String!, $repo: String!, $after: String, $includeLastRelease: Boolean!) {
 2 | 	repository(owner: $owner, name: $repo) {
 3 | 		...commits
 4 | 		...lastRelease @include(if:$includeLastRelease)
 5 | 	    # for some reason releases aren't queryable on forked repos if they weren't published on the fork first
 6 | 	    # so include the parent in case the repo doesn't have a last release
 7 | 	    parent @include(if: $includeLastRelease) {
 8 | 			...lastRelease
 9 | 	    }
10 | 	}
11 | }
12 | 
13 | fragment commits on Repository {
14 | 	ref(qualifiedName: "refs/heads/master") {
15 | 		target {
16 | 			... on Commit {
17 | 				history(first: 100, after: $after) {
18 | 					pageInfo {
19 | 						hasNextPage
20 | 						endCursor
21 | 					}
22 | 					nodes {
23 | 						url
24 | 						messageHeadline
25 | 						oid
26 | 					}
27 | 				}
28 | 			}
29 | 		}
30 | 	}
31 | }
32 | 
33 | fragment lastRelease on Repository {
34 |     releases(last: 1) {
35 |     	nodes {
36 |     	 	description
37 |     	 	tag {
38 |     	    	name
39 |     	    	target {
40 |     	    	  oid
41 |     	    	}
42 | 	 		}
43 |  		}
44 | 	}
45 | }


--------------------------------------------------------------------------------
/build/publish/github-graphql.js:
--------------------------------------------------------------------------------
  1 | var fetch = require('node-fetch');
  2 | var fs = require('fs');
  3 | var path = require('path');
  4 | 
  5 | var files = {};
  6 | 
  7 | function readFileForMethod(method) {
  8 | 	return new Promise(function (resolve, reject) {
  9 | 		if (files[method]) {
 10 | 			return setTimeout(resolve.bind(null, files[method]));
 11 | 		}
 12 | 		fs.readFile(path.resolve(__dirname, method + '.graphql'), function (err, file) {
 13 | 			if (err) {
 14 | 				return reject(err);
 15 | 			}
 16 | 			files[method] = file.toString().replace('\n', '');
 17 | 			resolve(files[method]);
 18 | 		});
 19 | 	});
 20 | }
 21 | 
 22 | function queryGraphQL (method, vars, key) {
 23 | 	return readFileForMethod(method).then(function (query) {
 24 | 		var body = JSON.stringify({query: query, variables: vars});
 25 | 		return fetch('https://api.github.com/graphql', {
 26 | 			method: 'POST',
 27 | 			headers: {
 28 | 				Authorization: 'bearer ' + key
 29 | 			},
 30 | 			body: body
 31 | 		}).then(res => res.json()).then(function (result) {
 32 | 			if (result.errors && result.errors.length > 0) {
 33 | 				result.errors.forEach(console.error);
 34 | 				throw new Error(result.error[0]);
 35 | 			}
 36 | 			return result;
 37 | 		});
 38 | 	});
 39 | }
 40 | 
 41 | function hasProperties(obj) {
 42 | 	for (var key in obj) {
 43 | 		return true;
 44 | 	}
 45 | 	return false;
 46 | }
 47 | 
 48 | function GithubGraphQLWrapper (key, owner, repo) {
 49 | 	this._key = key;
 50 | 	this._repo = repo;
 51 | 	this._owner = owner;
 52 | 	this._commits = [];
 53 | 	this._issues = [];
 54 | 	this._mergedPRs = [];
 55 | 	this._closedIssues = [];
 56 | 	this._lastRelease = null;
 57 | 	this._commitAfterRef = null;
 58 | 	this._reachedLastIssue = false;
 59 | 	this._reachedLastPr = false;
 60 | 	this._afterPRRef = null;
 61 | 	this._afterIssueRef = null;
 62 | }
 63 | 
 64 | GithubGraphQLWrapper.prototype = {
 65 | 	constructor: GithubGraphQLWrapper,
 66 | 	fetchLastGithubRelease: function fetchLastGithubRelease () {
 67 | 		return queryGraphQL('GetMasterCommits', {
 68 | 			repo: this._repo,
 69 | 			owner: this._owner,
 70 | 			includeLastRelease: true,
 71 | 			after: this._commitAfterRef
 72 | 		}, this._key).then(result => {
 73 | 			var data = result.data.repository;
 74 | 			var parentRelease = data.parent && data.parent.releases.nodes.length && data.parent.releases.nodes[0];
 75 | 			var lastRelease = data.releases.nodes.length > 0 ? data.releases.nodes[0] : parentRelease;
 76 | 			var history = data.ref.target.history;
 77 | 			this._lastRelease = lastRelease;
 78 | 			this._commits = this._commits.concat(history.nodes);
 79 | 			this._commitAfterRef = history.pageInfo.endCursor;
 80 | 			return this;
 81 | 		});
 82 | 	},
 83 | 	fetchCommitsToLastRelease: function () {
 84 | 		return queryGraphQL('GetMasterCommits', {
 85 | 			repo: this._repo,
 86 | 			owner: this._owner,
 87 | 			includeLastRelease: false,
 88 | 			after: this._commitAfterRef
 89 | 		}, this._key).then(result => {
 90 | 			var data = result.data.repository;
 91 | 			var history = data.ref.target.history;
 92 | 			this._commitAfterRef = data.ref.target.history.pageInfo.endCursor;
 93 | 			this._commits = this._commits.concat(data.ref.target.history.nodes);
 94 | 			var commitOids = this._commits.map(c => c.oid);
 95 | 			if (commitOids.indexOf(this._lastRelease.tag.target.oid) == -1 && history.pageInfo.hasNextPage) {
 96 | 				return this.fetchCommitsToLastRelease();
 97 | 			}
 98 | 			this._commits.splice(commitOids.indexOf(this._lastRelease.tag.target.oid));
 99 | 			return this;
100 | 		});
101 | 	},
102 | 	fetchPRsAndIssues: function () {
103 | 		return queryGraphQL('GetClosedIssuesAndMergedPRs', {
104 | 			repo: this._repo,
105 | 			owner: this._owner,
106 | 			includePRs: !this._reachedLastPr,
107 | 			includeIssues: !this._reachedLastIssue,
108 | 			afterIssues: this._afterIssueRef,
109 | 			afterPRs: this._afterPRRef,
110 | 		}, this._key).then(result => {
111 | 			var repository = result.data.repository;
112 | 			var parent = repository.parent;
113 | 			var parentIssues = parent && parent.issues && parent.issues.nodes.length && parent.issues;
114 | 			var localIssues = repository.issues && repository.issues.nodes.length && repository.issues;
115 | 			var issues = localIssues || parentIssues;
116 | 			var parentPRs = parent && parent.pullRequests && parent.pullRequests.nodes.length && parent.pullRequests;
117 | 			var localPRs = repository.pullRequests && repository.pullRequests.nodes.length && repository.pullRequests;
118 | 			var prs = localPRs || parentPRs;
119 | 			if (issues) {
120 | 				this._reachedLastIssue = !issues.pageInfo.hasNextPage;
121 | 				this._afterIssueRef = issues.pageInfo.endCursor;
122 | 				this._closedIssues = this._closedIssues.concat(issues.nodes);
123 | 			}
124 | 
125 | 			if (prs) {
126 | 				this._reachedLastPr = !prs.pageInfo.hasNextPage;
127 | 				this._afterPRRef = prs.pageInfo.endCursor;
128 | 				this._mergedPRs = this._mergedPRs.concat(prs.nodes);
129 | 			}
130 | 			if (!this._reachedLastPr && !this._reachedLastIssue) {
131 | 				return this.fetchPRsAndIssues();
132 | 			}
133 | 		}).then(() => {
134 | 			this._closedIssues = this._closedIssues.map(issue => {
135 | 				issue.timeline = issue.timeline.nodes.filter(hasProperties);
136 | 				return issue;
137 | 			}).filter(issue => issue.timeline.length > 0);
138 | 			this._mergedPRs.map(pr => {
139 | 				pr.timeline = pr.timeline.nodes.filter(hasProperties);
140 | 				return pr;
141 | 			}).filter(pr => pr.timeline.length > 0);
142 | 			return this;
143 | 		});
144 | 	},
145 | 	getLastRelease: function () {
146 | 		return this._lastRelease;
147 | 	},
148 | 	getMergedPRs: function () {
149 | 		return this._mergedPRs;
150 | 	},
151 | 	getCommits: function () {
152 | 		return this._commits;
153 | 	},
154 | 	getClosedIssues: function () {
155 | 		return this._closedIssues;
156 | 	},
157 | 	getOwner: function () {
158 | 		return this._owner;
159 | 	},
160 | 	getRepo: function () {
161 | 		return this._repo;
162 | 	}
163 | };
164 | 
165 | module.exports = GithubGraphQLWrapper;


--------------------------------------------------------------------------------
/build/tasks/bower.js:
--------------------------------------------------------------------------------
 1 | module.exports = function (grunt) {
 2 |   var bowerJSON = {
 3 |     name: 'blocks',
 4 |     main: 'dist/blocks.js',
 5 |     license: 'MIT',
 6 |     author: {
 7 |       name: 'Antonio Stoilkov',
 8 |       email: 'antonio.stoilkov@gmail.com',
 9 |       url: 'http://jsblocks.com'
10 |     },
11 |     ignore: [
12 |       'build',
13 |       'examples',
14 |       'lib',
15 |       'node_modules',
16 |       'src',
17 |       'test',
18 |       '*.gitignore',
19 |       'Gruntile.js',
20 |       'package.json',
21 |       'dist/npm'
22 |     ],
23 |     keywords: []
24 |   };
25 | 
26 |   grunt.registerTask('bower', function () {
27 |     grunt.file.write('bower.json', JSON.stringify(bowerJSON, null, 4));
28 |   });
29 | };
30 | 


--------------------------------------------------------------------------------
/build/tasks/build-test-definitions.js:
--------------------------------------------------------------------------------
 1 | module.exports = function (grunt) {
 2 |   grunt.registerTask('build-tests-definitions', function () {
 3 |     var tests = {};
 4 |     var contents;
 5 |     var key;
 6 | 
 7 |     grunt.file.recurse('test/spec', function (abspath) {
 8 |       placeInObject(tests, abspath.replace('test/', ''), abspath.replace('test/spec/', ''));
 9 |     });
10 | 
11 |     for (key in tests) {
12 |       tests['blocks.' + key] = tests[key];
13 |       delete tests[key];
14 |     }
15 | 
16 |     contents = JSON.stringify(tests, null, 4);
17 | 
18 |     grunt.file.write('test/tests.json', contents);
19 |   });
20 | 
21 |   function placeInObject(current, fullPath, currentPath) {
22 |     var parts = currentPath.split('/');
23 |     var firstPart = parts[0];
24 |     var obj;
25 | 
26 |     if (parts.length == 2) {
27 |       (current[firstPart] = current[firstPart] || []).push(fullPath);
28 |     } else {
29 |       obj = current[firstPart] = current[firstPart] || {};
30 |       if (obj instanceof Array) {
31 |         obj.push({});
32 |         obj = obj[obj.length - 1];
33 |       }
34 |       placeInObject(obj, fullPath, parts.slice(1).join('/'));
35 |     }
36 |   }
37 | };


--------------------------------------------------------------------------------
/build/tasks/build.js:
--------------------------------------------------------------------------------
  1 | module.exports = function (grunt) {
  2 |   var esprima = require('esprima');
  3 |   var escodegen = require('escodegen');
  4 |   var estrvarse = require('estraverse');
  5 |   var definedModuleNames = {};
  6 |   var requirejsConfig = {};
  7 |   var requirejsOptions = {
  8 |     baseUrl: 'src',
  9 |     out: 'dist/<%= name %>/blocks-<%= name %>.js',
 10 |     include: ['<%= name %>.js'],
 11 |     optimize: 'none',
 12 |     skipSemiColonInsertion: true,
 13 |     onBuildWrite: function (name, path, contents) {
 14 |      var rdefineEnd = /\}\);[^}\w]*$/;
 15 | 
 16 |       if (/.\/var\//.test(path)) {
 17 |         contents = contents
 18 |           .replace(/define\([\w\W]*?return/, '    var ' + (/var\/([\w-]+)/.exec(name)[1]) + ' =')
 19 |           .replace(rdefineEnd, '');
 20 | 
 21 |       } else {
 22 |         contents = contents
 23 |           .replace(/\/\*\s*ExcludeStart\s*\*\/[\w\W]*?\/\*\s*ExcludeEnd\s*\*\//ig, '')
 24 |           .replace(/\/\/\s*BuildExclude\n\r?[\w\W]*?\n\r?/ig, '');
 25 |         var ast = esprima.parse(contents, {
 26 |           tokens: true,
 27 |           comment: true,
 28 |           range: true
 29 |         });
 30 | 
 31 |         estrvarse.attachComments(ast, ast.comments, ast.tokens);
 32 | 
 33 |         if (ast.body[0].expression.callee.name == 'define') {
 34 |           var moduleExpression = findModuleExpressionArgument(ast.body[0].expression.arguments);
 35 |           if (!moduleExpression || !moduleExpression.body.body[0] || moduleExpression.body.body[0].type == 'ReturnStatement') {
 36 |             // Null out empty define statements e.g. define(['./query/ready', '...'])
 37 |             // and expresions without an expression or only an return statement e.g. define([], function () { return blocks; })
 38 |             contents = '';
 39 |           } else {
 40 |             var moduleName;
 41 |             try {
 42 |               moduleName = findModuleExportIdentifier(moduleExpression.body.body) || /\/(\w+).?j?s?$/.exec(name)[1];
 43 |             } catch(e) {}
 44 |             if (moduleName && definedModuleNames[moduleName] && definedModuleNames[moduleName] != path) {
 45 |               grunt.fail.warn('[NamingConflict]: Module ' + path + ' tried to define ' + moduleName + ' which is already defined by ' + definedModuleNames[moduleName] + ' !');
 46 |             } else if (moduleName){
 47 |               definedModuleNames[moduleName] = path;
 48 |             }
 49 |             ast =  wrapModuleAst(moduleExpression, moduleName);
 50 |             contents = escodegen.generate(ast, {
 51 |               format: {
 52 |                 indent: {
 53 |                   style: '  ',
 54 |                   base: 0,
 55 |                   adjustMultilineComment: true
 56 |                 }
 57 |               },
 58 |               comment: true
 59 |             });
 60 |           }
 61 |         }
 62 |        /* contents = contents
 63 |           .replace(/\s*return\s+[^\}]+(\}\);[^\w\}]*)$/, '$1')
 64 |           // Multiple exports
 65 |           .replace(/\s*exports\.\w+\s*=\s*\w+;/g, '');
 66 | 
 67 |         // Remove define wrappers, closure ends, and empty declarations
 68 |         contents = contents
 69 |           .replace(/define\([^{]*?{/, '')
 70 |           .replace(rdefineEnd, '');
 71 | 
 72 |         // Remove anything wrapped with
 73 |         // /* ExcludeStart */ /* ExcludeEnd */
 74 |         // or a single line directly after a // BuildExclude comment
 75 | 
 76 |         // Remove empty definitions
 77 |       /*  contents = contents
 78 |           .replace(/define\(\[[^\]]+\]\)[\W\n]+$/, '');*/
 79 | 
 80 |       }
 81 | 
 82 |       return contents;
 83 |     }
 84 |   };
 85 | 
 86 |   function findModuleExportIdentifier (module) {
 87 |     for (var i = module.length -1 ; i >= 0; i--) {
 88 |       var expression = module[i];
 89 |       if (expression.type == 'ReturnStatement') {
 90 |         return expression.argument.name;
 91 |       }
 92 |     }
 93 |    throw new Error('No return statement');
 94 |   }
 95 | 
 96 |   function findModuleExpressionArgument(args) {
 97 |     for (var i in args) {
 98 |       var arg = args[i];
 99 |       if (arg.type == 'FunctionExpression') {
100 |         return arg;
101 |       }
102 |     }
103 |   }
104 | 
105 | 
106 |   function wrapModuleAst (node, exportName) {
107 |       var wrapedModule;
108 |       var bodyNode;
109 |       if (exportName) {
110 |         wrapedModule = esprima.parse('var ' + exportName + ' = (function () { })();');
111 |         bodyNode = wrapedModule.body[0].declarations[0].init.callee.body;
112 |       } else {
113 |         wrapedModule = esprima.parse('(function () { })();');
114 |         bodyNode = wrapedModule.body[0].expression.callee.body;
115 |       }
116 |       // insert body of the original "define"-function to the 
117 |       bodyNode.body = node.body.body;
118 |       return wrapedModule;
119 |   }
120 | 
121 |   var names = ['query', 'mvc', 'node'];
122 |   names.forEach(function (name) {
123 |     grunt.config.set('name', name);
124 |     (requirejsConfig[name] = {}).options = grunt.config.process(requirejsOptions);
125 |     grunt.config.set('name', undefined);
126 |   });
127 | 
128 |   grunt.config.set('requirejs', requirejsConfig);
129 | 
130 |   grunt.registerTask('build', function () {
131 |     var tasks = [];
132 |     for (var i = 0; i < arguments.length; i++) {
133 |       tasks.push('requirejs:' + arguments[i]);
134 |     }
135 |     grunt.task.run(tasks.length ? tasks : 'requirejs');
136 |   });
137 | };


--------------------------------------------------------------------------------
/build/tasks/combine.js:
--------------------------------------------------------------------------------
 1 | module.exports = function (grunt) {
 2 | 
 3 |   grunt.registerTask('combine', function () {
 4 |     var core = grunt.file.read('lib/blocks/core.js').replace('@version', grunt.config.get('version'));
 5 |     var jsvalue = grunt.file.read('lib/blocks/value-nocore.js');
 6 |     var mvc = grunt.file.read('dist/mvc/blocks-mvc.js');
 7 |     var query = grunt.file.read('dist/query/blocks-query.js');
 8 | 
 9 |     // TODO: Remove if node is not created until released
10 |     var node = grunt.file.read('dist/node/blocks-node.js');
11 | 
12 |     var nodeCode = insertSourceCode(
13 |       core.replace('typeof window !== \'undefined\' ? window : this', 'typeof window !== \'undefined\' && !window.__mock__ ? window : this'),
14 |       [jsvalue, node]);
15 |     grunt.file.write('dist/node/blocks-node.js', nodeCode);
16 | 
17 | 
18 |     var jsblocks = insertSourceCode(core, [jsvalue, mvc]);
19 |     var mvcOnly = insertSourceCode(core, [mvc]);
20 |     var queryOnly = insertSourceCode(core, [query]);
21 |     var queryAndValue = insertSourceCode(core, [jsvalue, query]);
22 | 
23 |     grunt.file.write('dist/blocks-source.js', jsblocks);
24 |     grunt.file.write('dist/mvc/blocks-mvc.js', mvcOnly);
25 |     grunt.file.write('dist/query/blocks-query.js', queryOnly);
26 |     grunt.file.write('dist/query/blocks-query-data.js', queryAndValue);
27 |   });
28 | 
29 |   function getSourceCodeWrap(code) {
30 |     return '(function () {\n' + code + '\n})();'
31 |   }
32 | 
33 |   function insertSourceCode(core, code) {
34 |     var sourceCodeLocation;
35 |     var result = core;
36 | 
37 |     code.forEach(function (codeBlock) {
38 |       sourceCodeLocation = result.indexOf('// @source-code');
39 |       result = result.substring(0, sourceCodeLocation) + '\n' + getSourceCodeWrap(codeBlock) + result.substring(sourceCodeLocation);
40 |     });
41 | 
42 |     return result;
43 |   }
44 | };
45 | 


--------------------------------------------------------------------------------
/build/tasks/npm.js:
--------------------------------------------------------------------------------
 1 | module.exports = function (grunt) {
 2 |   var packageJSON = {
 3 |     name: 'blocks',
 4 |     version: grunt.config.get('version'),
 5 |     description: 'jsblocks - Better MV-ish Framework',
 6 |     main: 'node/blocks-node.js',
 7 |     keyword: ['MVC', 'MVVM', 'MVW', 'server rendering', 'filtering', 'sorting', 'paging', 'framework'],
 8 |     scripts: {
 9 |       'test': 'echo \'Error: no test specified\' && exit 1'
10 |     },
11 |     repository: {
12 |       type: 'git',
13 |       url: 'https://github.com/astoilkov/jsblocks.git'
14 |     },
15 |     author: {
16 |       name: 'Antonio Stoilkov',
17 |       email: 'antonio.stoilkov@gmail.com',
18 |       url: 'http://jsblocks.com'
19 |     },
20 |     license: 'MIT',
21 |     bugs: {
22 |       url: 'https://github.com/astoilkov/jsblocks/issues',
23 |       email: 'support@jsblocks.com'
24 |     },
25 |     homepage: 'https://github.com/astoilkov/jsblocks',
26 |     dependencies: {
27 |       express: "4.14.0",
28 |       parse5: "2.2.1"
29 |     }
30 |   };
31 | 
32 |   grunt.registerTask('npm', function () {
33 |     grunt.file.recurse('dist', function (abspath, rootdir, subdir, filename) {
34 |       var subFolder = subdir ? subdir + '/' : '';
35 |       subdir = subdir || '';
36 | 
37 |       if (subdir.indexOf('npm') == -1) {
38 |         grunt.file.write('dist/npm/' + subFolder + filename, grunt.file.read(abspath));
39 |       }
40 |     });
41 |     grunt.file.write('dist/npm/package.json', JSON.stringify(packageJSON, null, 4));
42 |     grunt.file.write('dist/npm/README.md', grunt.file.read('README.md'));
43 |   });
44 | };


--------------------------------------------------------------------------------
/build/tasks/test.js:
--------------------------------------------------------------------------------
  1 | module.exports = function (grunt) {
  2 |   var karmaConfig = {
  3 |     options: {
  4 |       files: [
  5 |         // code for testing
  6 |         'dist/blocks.js',
  7 | 
  8 |         // dependencies
  9 |         require.resolve('jquery/dist/jquery.js'),
 10 |         //'lib/jquery-1.11.2/jquery-1.11.2.js',
 11 |         require.resolve('jasmine-jquery'),
 12 |         //'lib/jasmine-jquery-2.2.0/jasmine-jquery.js',
 13 |         'test/blocks.testing.js',
 14 | 
 15 |         // tests location
 16 |         'test/spec/**/*.js'
 17 |       ],
 18 |       autoWatch: false,
 19 |       //browserNoActivityTimeout: 30000,
 20 |       // browserDisconnectTimeout
 21 |       // browserDisconnectTolerance
 22 |       frameworks: ['jasmine']
 23 |     },
 24 | 
 25 |     test: {
 26 |       browsers: ['Chrome', 'Firefox', 'IE11', 'IE10'],
 27 |       customLaunchers: {
 28 |         IE11: {
 29 |           base: 'IE',
 30 |           'x-ua-compatible': 'IE=EmulateIE11'
 31 |         },
 32 |         IE10: {
 33 |           base: 'IE',
 34 |           'x-ua-compatible': 'IE=EmulateIE10'
 35 |         }
 36 |       },
 37 |       singleRun: true
 38 |     },
 39 | 
 40 |     coverage: {
 41 |       browserConsoleLogOptions: {
 42 |         terminal: false
 43 |       },
 44 |       reporters: ['dots', 'coverage'],
 45 |       preprocessors: {
 46 |         'dist/blocks.js': ['coverage']
 47 |       },
 48 |       coverageReporter: {
 49 |         type: 'html',
 50 |         dir: 'coverage/'
 51 |       },
 52 |       browsers: ['Firefox'],
 53 |       singleRun: true
 54 |     },
 55 | 
 56 |     watch: {
 57 |       browsers: ['PhantomJS'],
 58 |       background: true
 59 |     },
 60 |     phantom: {
 61 |       browsers: ['PhantomJS'],
 62 |       singleRun: true
 63 |     },
 64 | 
 65 |     chrome: {
 66 |       browsers: ['Chrome'],
 67 |       singleRun: true
 68 |     },
 69 | 
 70 |     firefox: {
 71 |       browsers: ['Firefox'],
 72 |       singleRun: true
 73 |     },
 74 | 
 75 |     ie: {
 76 |       browsers: ['IE11', 'IE10', 'IE9'],
 77 |       customLaunchers: {
 78 |         IE11: {
 79 |           base: 'IE',
 80 |           'x-ua-compatible': 'IE=EmulateIE11'
 81 |         },
 82 |         IE10: {
 83 |           base: 'IE',
 84 |           'x-ua-compatible': 'IE=EmulateIE10'
 85 |         },
 86 |         IE9: {
 87 |           base: 'IE',
 88 |           'x-ua-compatible': 'IE=EmulateIE9'
 89 |         },
 90 |         IE8: {
 91 |           base: 'IE',
 92 |           'x-ua-compatible': 'IE=EmulateIE8'
 93 |         }
 94 |       },
 95 |       singleRun: true
 96 |     },
 97 | 
 98 |     ie9: {
 99 |       browsers: ['IE9'],
100 |       customLaunchers: {
101 |         IE9: {
102 |           base: 'IE',
103 |           'x-ua-compatible': 'IE=EmulateIE9'
104 |         }
105 |       },
106 |       singleRun: true
107 |     },
108 | 
109 |     safari: {
110 |       browsers: ['Safari'],
111 |       singleRun: true
112 |     },
113 | 
114 |     opera: {
115 |       browsers: ['Opera'],
116 |       singleRun: true
117 |     },
118 | 
119 | 
120 |     build: {
121 |       browsers: ['IE', 'ChromeCanary', 'Safari', 'Firefox', 'Chrome', 'PhantomJS'],
122 |       singleRun: true
123 |     }
124 |   };
125 | 
126 |   grunt.config.set('karma', karmaConfig);
127 | 
128 |   grunt.registerTask('test', function (browser) {
129 |     if (browser) {
130 |       grunt.task.run('karma:' + browser);
131 |     } else {
132 |       grunt.task.run('karma:test');
133 |     }
134 |   });
135 | };


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "jsblocks",
 3 |   "version": "0.3.5",
 4 |   "description": "Better MV-ish Framework",
 5 |   "author": {
 6 |     "name": "Antonio Stoilkov",
 7 |     "email": "antonio.stoilkov@gmail.com",
 8 |     "url": "http://jsblocks.com"
 9 |   },
10 |   "license": "MIT",
11 |   "scripts": {
12 |     "test": "grunt test:firefox"
13 |   },
14 |   "devDependencies": {
15 |     "blocks": "^0.3.4",
16 |     "escodegen": "^1.6.1",
17 |     "esprima": "^3.0.0",
18 |     "estraverse": "^4.1.0",
19 |     "grunt": "^1.0.1",
20 |     "grunt-cli": "^1.2.0",
21 |     "grunt-contrib-jshint": "^1.0.0",
22 |     "grunt-contrib-requirejs": "^1.0.0",
23 |     "grunt-contrib-uglify": "^2.0.0",
24 |     "grunt-contrib-watch": "^1.0.0",
25 |     "grunt-karma": "^2.0.0",
26 |     "grunt-notify": "^0.4.1",
27 |     "grunt-preprocess": "^5.1.0",
28 |     "highlight.js": "^9.6.0",
29 |     "jasmine-core": "^2.2.0",
30 |     "jasmine-jquery": "^2.1.1",
31 |     "jquery": "^3.2.1",
32 |     "karma": "^1.2.0",
33 |     "karma-chrome-launcher": "^2.0.0",
34 |     "karma-coverage": "^1.1.1",
35 |     "karma-firefox-launcher": "^1.0.0",
36 |     "karma-ie-launcher": "^1.0.0",
37 |     "karma-jasmine": "^1.0.2",
38 |     "karma-jasmine-jquery": "^0.1.1",
39 |     "node-fetch": "^1.6.3",
40 |     "nodegit": "^0.18.0",
41 |     "npm-utils": "^1.11.0",
42 |     "parse5": "^2.2.1"
43 |   }
44 | }
45 | 


--------------------------------------------------------------------------------
/src/.jshintrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "eqeqeq": false,
 3 |   "forin": false,
 4 |   "plusplus": false,
 5 |   "strict": false,
 6 |   "newcap": false,
 7 | 
 8 |   "es3": true,
 9 |   "bitwise": true,
10 |   "camelcase": true,
11 |   "curly": true,
12 |   "freeze": true,
13 |   "immed": true,
14 |   "latedef": false,
15 |   "noarg": true,
16 |   "noempty": true,
17 |   "nonbsp": true,
18 |   "nonew": true,
19 |   "undef": true,
20 |   "unused": true,
21 |   "trailing": true,
22 |   "loopfunc": true,
23 |   "eqnull": true,
24 |   "quotmark": "single",
25 | 
26 |   "browser": true,
27 | 
28 |   "globals": {
29 |     "blocks": true,
30 |     "define": true,
31 |     "global": true
32 |   }
33 | }
34 | 


--------------------------------------------------------------------------------
/src/DataSource.js:
--------------------------------------------------------------------------------
1 | define([
2 |   './dataSource/DataSource'
3 | ], function (DataSource) {
4 |     return DataSource;
5 | });


--------------------------------------------------------------------------------
/src/core.js:
--------------------------------------------------------------------------------
1 | define([
2 |   
3 | ], function (blocks) {
4 |     return blocks;
5 | });
6 | 


--------------------------------------------------------------------------------
/src/modules/Escape.js:
--------------------------------------------------------------------------------
 1 | define([],
 2 | function () {
 3 |     var htmlEntityMap = {
 4 |         '&': '&amp;',
 5 |         '<': '&lt;',
 6 |         '>': '&gt;',
 7 |         '"': '&quot;',
 8 |         '\'': '&#x27;',
 9 |         '/': '&#x2F;'
10 |     };
11 | 
12 |     var htmlEscapeRegEx = (function () {
13 |         var entities = [];
14 |         for (var entity in htmlEntityMap) {
15 |             entities.push(entity);
16 |         }
17 |         return new RegExp('(' + entities.join('|') + ')', 'g');
18 |     })();
19 | 
20 |     function internalHTMLEscapeReplacer(entity) {
21 |         return htmlEntityMap[entity];
22 |     }
23 | 
24 |     var Escape = {
25 |         // moved from modules/escapeRegEx
26 |         forRegEx: function escapeRegEx(string) {
27 |             return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\
amp;');
28 |         },
29 |         forHTML: function (value) {
30 |             if (blocks.isString(value)) {
31 |                 return value.replace(htmlEscapeRegEx, internalHTMLEscapeReplacer);
32 |             }
33 |             return value;
34 |         },
35 |         // This is only valid because jsblocks forces (inserts itself) double quotes for attributes
36 |         // don't use this in other cases
37 |         forHTMLAttributes: function (value) {
38 |             if (blocks.isString(value)) {
39 |                 return value.replace(/"/g, '&quot;');
40 |             }
41 |             return value;
42 |         }
43 |     };
44 |     return Escape;
45 | });
46 | 


--------------------------------------------------------------------------------
/src/modules/Event.js:
--------------------------------------------------------------------------------
  1 | define(function () {
  2 |   var isMouseEventRegEx = /^(?:mouse|pointer|contextmenu)|click/;
  3 |   var isKeyEventRegEx = /^key/;
  4 | 
  5 |   function returnFalse() {
  6 |     return false;
  7 |   }
  8 | 
  9 |   function returnTrue() {
 10 |     return true;
 11 |   }
 12 | 
 13 |   function Event(e) {
 14 |     this.originalEvent = e;
 15 |     this.type = e.type;
 16 | 
 17 |     this.isDefaultPrevented = e.defaultPrevented ||
 18 |         (e.defaultPrevented === undefined &&
 19 |         // Support: IE < 9, Android < 4.0
 20 |         e.returnValue === false) ?
 21 |         returnTrue :
 22 |         returnFalse;
 23 | 
 24 |     this.timeStamp = e.timeStamp || +new Date();
 25 |   }
 26 | 
 27 |   Event.PropertiesToCopy = {
 28 |     all: 'altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which'.split(' '),
 29 |     mouse: 'button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement'.split(' '),
 30 |     keyboard: 'char charCode key keyCode'.split(' ')
 31 |   };
 32 | 
 33 |   Event.CopyProperties = function (originalEvent, event, propertiesName) {
 34 |     blocks.each(Event.PropertiesToCopy[propertiesName], function (propertyName) {
 35 |       event[propertyName] = originalEvent[propertyName];
 36 |     });
 37 |   };
 38 | 
 39 |   Event.prototype = {
 40 |     preventDefault: function () {
 41 |         var e = this.originalEvent;
 42 | 
 43 |         this.isDefaultPrevented = returnTrue;
 44 | 
 45 |         if (e.preventDefault) {
 46 |             // If preventDefault exists, run it on the original event
 47 |             e.preventDefault();
 48 |         } else {
 49 |             // Support: IE
 50 |             // Otherwise set the returnValue property of the original event to false
 51 |             e.returnValue = false;
 52 |         }
 53 |     },
 54 | 
 55 |     stopPropagation: function () {
 56 |         var e = this.originalEvent;
 57 | 
 58 |         this.isPropagationStopped = returnTrue;
 59 | 
 60 |         // If stopPropagation exists, run it on the original event
 61 |         if (e.stopPropagation) {
 62 |             e.stopPropagation();
 63 |         }
 64 | 
 65 |         // Support: IE
 66 |         // Set the cancelBubble property of the original event to true
 67 |         e.cancelBubble = true;
 68 |     },
 69 | 
 70 |     stopImmediatePropagation: function () {
 71 |         var e = this.originalEvent;
 72 | 
 73 |         this.isImmediatePropagationStopped = returnTrue;
 74 | 
 75 |         if (e.stopImmediatePropagation) {
 76 |             e.stopImmediatePropagation();
 77 |         }
 78 | 
 79 |         this.stopPropagation();
 80 |     }
 81 |   };
 82 | 
 83 |   Event.fix = function (originalEvent) {
 84 |     var type = originalEvent.type;
 85 |     var event = new Event(originalEvent);
 86 | 
 87 |     Event.CopyProperties(originalEvent, event, 'all');
 88 | 
 89 |     // Support: IE<9
 90 |     // Fix target property (#1925)
 91 |     if (!event.target) {
 92 |         event.target = originalEvent.srcElement || document;
 93 |     }
 94 | 
 95 |     // Support: Chrome 23+, Safari?
 96 |     // Target should not be a text node (#504, #13143)
 97 |     if (event.target.nodeType === 3) {
 98 |         event.target = event.target.parentNode;
 99 |     }
100 | 
101 |     // Support: IE<9
102 |     // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
103 |     event.metaKey = !!event.metaKey;
104 | 
105 |     if (isMouseEventRegEx.test(type)) {
106 |         Event.fixMouse(originalEvent, event);
107 |     } else if (isKeyEventRegEx.test(type) && event.which == null) {
108 |         Event.CopyProperties(originalEvent, event, 'keyboard');
109 |         // Add which for key events
110 |         event.which = originalEvent.charCode != null ? originalEvent.charCode : originalEvent.keyCode;
111 |     }
112 | 
113 |     return event;
114 |   };
115 | 
116 |   Event.fixMouse = function (originalEvent, event) {
117 |     var button = originalEvent.button;
118 |     var fromElement = originalEvent.fromElement;
119 |     var body;
120 |     var eventDoc;
121 |     var doc;
122 | 
123 |     Event.CopyProperties(originalEvent, event, 'mouse');
124 | 
125 |     // Calculate pageX/Y if missing and clientX/Y available
126 |     if (event.pageX == null && originalEvent.clientX != null) {
127 |         eventDoc = event.target.ownerDocument || document;
128 |         doc = eventDoc.documentElement;
129 |         body = eventDoc.body;
130 | 
131 |         event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
132 |         event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
133 |     }
134 | 
135 |     // Add relatedTarget, if necessary
136 |     if (!event.relatedTarget && fromElement) {
137 |         event.relatedTarget = fromElement === event.target ? originalEvent.toElement : fromElement;
138 |     }
139 | 
140 |     // Add which for click: 1 === left; 2 === middle; 3 === right
141 |     // Note: button is not normalized, so don't use it
142 |     if (!event.which && button !== undefined) {
143 |         /* jshint bitwise: false */
144 |         event.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));
145 |     }
146 |   };
147 | 
148 |   //var event = blocks.Event();
149 |   //event.currentTarget = 1; // the current element from which is the event is fired
150 |   //event.namespace = ''; // the namespace for the event
151 | 
152 |   return Event;
153 | });
154 | 


--------------------------------------------------------------------------------
/src/modules/Events.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core'
  3 | ], function (blocks) {
  4 |   var Events = (function () {
  5 |     function createEventMethod(eventName) {
  6 |       return function (callback, context) {
  7 |         if (arguments.length > 1) {
  8 |           Events.on(this, eventName, callback, context);
  9 |         } else {
 10 |           Events.on(this, eventName, callback);
 11 |         }
 12 |         return this;
 13 |       };
 14 |     }
 15 | 
 16 |     var methods = {
 17 |       on: function (eventName, callback, context) {
 18 |         if (arguments.length > 2) {
 19 |           Events.on(this, eventName, callback, context);
 20 |         } else {
 21 |           Events.on(this, eventName, callback);
 22 |         }
 23 |         return this;
 24 |       },
 25 | 
 26 |       once: function (eventNames, callback, thisArg) {
 27 |         Events.once(this, eventNames, callback, thisArg);
 28 |       },
 29 | 
 30 |       off: function (eventName, callback) {
 31 |         Events.off(this, eventName, callback);
 32 |       },
 33 | 
 34 |       trigger: function (eventName) {
 35 |         Events.trigger(this, eventName, blocks.toArray(arguments).slice(1, 100));
 36 |       }
 37 |     };
 38 |     methods._trigger = methods.trigger;
 39 | 
 40 |     return {
 41 |       register: function (object, eventNames) {
 42 |         eventNames = blocks.isArray(eventNames) ? eventNames : [eventNames];
 43 |         for (var i = 0; i < eventNames.length; i++) {
 44 |           var methodName = eventNames[i];
 45 |           if (methods[methodName]) {
 46 |             object[methodName] = methods[methodName];
 47 |           } else {
 48 |             object[methodName] = createEventMethod(methodName);
 49 |           }
 50 |         }
 51 |       },
 52 | 
 53 |       on: function (object, eventNames, callback, thisArg) {
 54 |         eventNames = blocks.toArray(eventNames).join(' ').split(' ');
 55 | 
 56 |         var i = 0;
 57 |         var length = eventNames.length;
 58 |         var eventName;
 59 | 
 60 |         if (!callback) {
 61 |           return;
 62 |         }
 63 | 
 64 |         if (!object._events) {
 65 |           object._events = {};
 66 |         }
 67 |         for (; i < length; i++) {
 68 |           eventName = eventNames[i];
 69 |           if (!object._events[eventName]) {
 70 |             object._events[eventName] = [];
 71 |           }
 72 |           object._events[eventName].push({
 73 |             callback: callback,
 74 |             thisArg: thisArg
 75 |           });
 76 |         }
 77 |       },
 78 | 
 79 |       once: function (object, eventNames, callback, thisArg) {
 80 |         Events.on(object, eventNames, callback, thisArg);
 81 |         Events.on(object, eventNames, function () {
 82 |           Events.off(object, eventNames, callback);
 83 |         });
 84 |       },
 85 | 
 86 |       off: function (object, eventName, callback) {
 87 |         if (blocks.isFunction(eventName)) {
 88 |           callback = eventName;
 89 |           eventName = undefined;
 90 |         }
 91 | 
 92 |         if (eventName !== undefined || callback !== undefined) {
 93 |           blocks.each(object._events, function (events, currentEventName) {
 94 |             if (eventName !== undefined && callback === undefined) {
 95 |               object._events[eventName] = [];
 96 |             } else {
 97 |               blocks.each(events, function (eventData, index) {
 98 |                 if (eventData.callback == callback) {
 99 |                   object._events[currentEventName].splice(index, 1);
100 |                   return false;
101 |                 }
102 |               });
103 |             }
104 |           });
105 |         } else {
106 |           object._events = undefined;
107 |         }
108 |       },
109 | 
110 |       trigger: function (object, eventName) {
111 |         var result = true;
112 |         var eventsData;
113 |         var thisArg;
114 |         var args;
115 | 
116 |         if (object && object._events) {
117 |           eventsData = object._events[eventName];
118 | 
119 |           if (eventsData && eventsData.length > 0) {
120 |             args = Array.prototype.slice.call(arguments, 2);
121 | 
122 |             blocks.each(eventsData, function iterateEventsData(eventData) {
123 |               if (eventData) {
124 |                 thisArg = object;
125 |                 if (eventData.thisArg !== undefined) {
126 |                   thisArg = eventData.thisArg;
127 |                 }
128 |                 if (eventData.callback.apply(thisArg, args) === false) {
129 |                   result = false;
130 |                 }
131 |               }
132 |             });
133 |           }
134 |         }
135 | 
136 |         return result;
137 |       },
138 | 
139 |       has: function (object, eventName) {
140 |         return !!blocks.access(object, '_events.' + eventName + '.length');
141 |       }
142 |     };
143 |   })();
144 | 
145 |   return Events;
146 | });


--------------------------------------------------------------------------------
/src/modules/Request.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../modules/uniqueId',
  4 |   '../query/serverData'
  5 | ], function (blocks, uniqueId, serverData) {
  6 |   function Request(options) {
  7 |     this.options = blocks.extend({}, Request.Defaults, options);
  8 |     this.execute();
  9 |   }
 10 | 
 11 |   Request.Execute = function (options) {
 12 |     return new Request(options);
 13 |   };
 14 | 
 15 |   Request.Defaults = {
 16 |     type: 'GET',
 17 |     url: '',
 18 |     processData: true,
 19 |     async: true,
 20 |     contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
 21 |     jsonp: 'callback',
 22 |     jsonpCallback: function () {
 23 |       return uniqueId();
 24 |     }
 25 | 
 26 |   /*
 27 |   timeout: 0,
 28 |   data: null,
 29 |   dataType: null,
 30 |   username: null,
 31 |   password: null,
 32 |   cache: null,
 33 |   throws: false,
 34 |   traditional: false,
 35 |   headers: {},
 36 |   */
 37 |   };
 38 | 
 39 |   Request.Accepts = {
 40 |     '*': '*/'.concat('*'),
 41 |     text: 'text/plain',
 42 |     html: 'text/html',
 43 |     xml: 'application/xml, text/xml',
 44 |     json: 'application/json, text/javascript'
 45 |   };
 46 | 
 47 |   Request.Meta = {
 48 |     statusFix: {
 49 |       // file protocol always yields status code 0, assume 200
 50 |       0: 200,
 51 |       // Support: IE9
 52 |       // IE sometimes returns 1223 instead of 204
 53 |       1223: 204
 54 |     }
 55 |   };
 56 | 
 57 |   Request.prototype = {
 58 |     execute: function () {
 59 |       var options = this.options;
 60 | 
 61 |       if (options.type == 'GET' && options.data) {
 62 |         this.appendDataToUrl(options.data);
 63 |       }
 64 | 
 65 |       if (serverData.hasData && serverData.data.requests && serverData.data.requests[options.url]) {
 66 |         setTimeout(function (self) {
 67 |           self.callSuccess(serverData.data.requests[options.url]);
 68 |         }, 5, this);
 69 |       } else {
 70 |         try {
 71 |           if (options.dataType == 'jsonp') {
 72 |             this.scriptRequest();
 73 |           } else {
 74 |             this.xhrRequest();
 75 |           }
 76 |         } catch (e) {
 77 | 
 78 |         }
 79 |       }
 80 |     },
 81 | 
 82 |     xhrRequest: function () {
 83 |       var options = this.options;
 84 |       var xhr = this.createXHR();
 85 | 
 86 |       xhr.onabort = blocks.bind(this.xhrError, this);
 87 |       xhr.ontimeout = blocks.bind(this.xhrError, this);
 88 |       xhr.onload = blocks.bind(this.xhrLoad, this);
 89 |       xhr.onerror = blocks.bind(this.xhrError, this);
 90 |       xhr.open(options.type.toUpperCase(), options.url, options.async, options.username, options.password);
 91 |       xhr.setRequestHeader('Content-Type', options.contentType);
 92 |       xhr.setRequestHeader('Accept', Request.Accepts[options.dataType || '*']);
 93 |       xhr.send(options.data || null);
 94 |     },
 95 | 
 96 |     createXHR: function () {
 97 |       var Type = XMLHttpRequest || window.ActiveXObject;
 98 |       try {
 99 |         return new Type('Microsoft.XMLHTTP');
100 |       } catch (e) {
101 | 
102 |       }
103 |     },
104 | 
105 |     xhrLoad: function (e) {
106 |       var request = e.target;
107 |       var status = Request.Meta.statusFix[request.status] || request.status;
108 |       var isSuccess = status >= 200 && status < 300 || status === 304;
109 |       if (isSuccess) {
110 |         this.callSuccess(request.responseText);
111 |       } else {
112 |         this.callError(request.statusText);
113 |       }
114 |     },
115 | 
116 |     xhrError: function () {
117 |       this.callError();
118 |     },
119 | 
120 |     scriptRequest: function () {
121 |       var that = this;
122 |       var options = this.options;
123 |       var script = document.createElement('script');
124 |       var jsonpCallback = {};
125 |       var callbackName = blocks.isFunction(options.jsonpCallback) ? options.jsonpCallback() : options.jsonpCallback;
126 | 
127 |       jsonpCallback[options.jsonp] = callbackName;
128 |       this.appendDataToUrl(jsonpCallback);
129 |       window[callbackName] = function (result) {
130 |         window[callbackName] = null;
131 |         that.scriptLoad(result);
132 |       };
133 | 
134 |       script.onerror = this.scriptError;
135 |       script.async = options.async;
136 |       script.src = options.url;
137 |       document.head.appendChild(script);
138 |     },
139 | 
140 |     scriptLoad: function (data) {
141 |       this.callSuccess(data);
142 |     },
143 | 
144 |     scriptError: function () {
145 |       this.callError();
146 |     },
147 | 
148 |     appendDataToUrl: function (data) {
149 |       var that = this;
150 |       var options = this.options;
151 |       var hasParameter = /\?/.test(options.url);
152 | 
153 |       if (blocks.isPlainObject(data)) {
154 |         blocks.each(data, function (value, key) {
155 |           options.url += that.append(hasParameter, key, value.toString());
156 |         });
157 |       } else if (blocks.isArray(data)) {
158 |         blocks.each(data, function (index, value) {
159 |           that.appendDataToUrl(value);
160 |         });
161 |       } else {
162 |         options.url += that.append(hasParameter, data.toString(), '');
163 |       }
164 |     },
165 | 
166 |     append: function (hasParameter, key, value) {
167 |       var result = hasParameter ? '&' : '?';
168 |       result += key;
169 |       if (value) {
170 |         result += '=' + value;
171 |       }
172 |       return result;
173 |     },
174 | 
175 |     callSuccess: function (data) {
176 |       var success = this.options.success;
177 |       var textStatus = 'success';
178 |       if (success) {
179 |         success(data, textStatus, null);
180 |       }
181 |       this.callComplete(textStatus);
182 |     },
183 | 
184 |     callError: function (errorThrown) {
185 |       var error = this.options.error;
186 |       var textStatus = 'error';
187 |       if (error) {
188 |         error(null, textStatus, errorThrown);
189 |       }
190 |       this.callComplete(textStatus);
191 |     },
192 | 
193 |     callComplete: function (textStatus) {
194 |       var complete = this.options.complete;
195 |       if (complete) {
196 |         complete(null, textStatus);
197 |       }
198 |     }
199 |   };
200 |   return Request;
201 | });
202 | 


--------------------------------------------------------------------------------
/src/modules/ajax.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   './Request'
 3 | ], function (Request) {
 4 |   function ajax(options) {
 5 |     if (window) {
 6 |       var jQuery = window.jQuery || window.$;
 7 |       if (jQuery && jQuery.ajax) {
 8 |         jQuery.ajax(options);
 9 |       } else {
10 |         Request.Execute(options);
11 |       }
12 |     }
13 |   }
14 | 
15 |   return ajax;
16 | });


--------------------------------------------------------------------------------
/src/modules/createProperty.js:
--------------------------------------------------------------------------------
 1 | define(function () {
 2 |   function createProperty(propertyName) {
 3 |     return function (value) {
 4 |       if (arguments.length === 0) {
 5 |         return this[propertyName];
 6 |       }
 7 |       this[propertyName] = value;
 8 |       return this;
 9 |     };
10 |   }
11 | 
12 |   return createProperty;
13 | });


--------------------------------------------------------------------------------
/src/modules/keys.js:
--------------------------------------------------------------------------------
 1 | define(function () {
 2 |   function keys(array) {
 3 |     var result = {};
 4 |     blocks.each(array, function (value) {
 5 |       result[value] = true;
 6 |     });
 7 |     return result;
 8 |   }
 9 | 
10 |   return keys;
11 | });


--------------------------------------------------------------------------------
/src/modules/parseCallback.js:
--------------------------------------------------------------------------------
 1 | define(function () {
 2 |   function parseCallback(callback, thisArg) {
 3 |     //callback = parseExpression(callback);
 4 |     if (thisArg != null) {
 5 |       var orgCallback = callback;
 6 |       callback = function (value, index, collection) {
 7 |         return orgCallback.call(thisArg, value, index, collection);
 8 |       };
 9 |     }
10 |     return callback;
11 |   }
12 | 
13 |   return parseCallback;
14 | });


--------------------------------------------------------------------------------
/src/modules/uniqueId.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core'
 3 | ], function (blocks) {
 4 |   var uniqueId = (function () {
 5 |     var timeStamp = Date.now();
 6 |     return function () {
 7 |       return 'blocks_' + blocks.version + '_' + timeStamp++;
 8 |     };
 9 |   })();
10 | 
11 |   return uniqueId;
12 | });
13 | 


--------------------------------------------------------------------------------
/src/mvc.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   './core',
 3 |   './query',
 4 |   './mvc/queries',
 5 |   './mvc/validators',
 6 |   './mvc/validation',
 7 |   './mvc/Application'
 8 | ], function () {
 9 | 
10 | });
11 | 


--------------------------------------------------------------------------------
/src/mvc/Property.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core'
 3 | ], function (blocks) {
 4 |   function Property(options) {
 5 |     this._options = options || {};
 6 |   }
 7 | 
 8 |   Property.Is = function (value) {
 9 |     return Property.prototype.isPrototypeOf(value);
10 |   };
11 | 
12 |   Property.Inflate = function (object) {
13 |     var properties = {};
14 |     var key;
15 |     var value;
16 |     var defaultValue;
17 | 
18 |     for (key in object) {
19 |       value = object[key];
20 |       if (Property.Is(value)) {
21 |         value = value._options;
22 |         defaultValue = value.defaultValue;
23 |         value.propertyName = key;
24 |         properties[value.field || key] = value;
25 |       }
26 |     }
27 | 
28 |     return properties;
29 |   };
30 | 
31 |   Property.Create = function (options, thisArg, value) {
32 |     var observable;
33 | 
34 |     if (arguments.length < 3) {
35 |       value = options.value || options.defaultValue;
36 |     }
37 |     thisArg = options.thisArg ? options.thisArg : thisArg;
38 | 
39 |     value = blocks.clone(value);
40 | 
41 |     observable = blocks
42 |       .observable(value, thisArg)
43 |       .extend('validation', options)
44 |       .on('changing', options.changing, thisArg)
45 |       .on('change', options.change, thisArg);
46 | 
47 |     if (options.extenders) {
48 |       blocks.each(options.extenders, function (extendee) {
49 |         observable = observable.extend.apply(observable, extendee);
50 |       });
51 |     }
52 | 
53 |     return observable;
54 |   };
55 | 
56 |   Property.prototype.extend = function () {
57 |     var options = this._options;
58 |     options.extenders = options.extenders || [];
59 |     options.extenders.push(blocks.toArray(arguments));
60 | 
61 |     return this;
62 |   };
63 |   return Property;
64 | });
65 | 


--------------------------------------------------------------------------------
/src/mvc/View.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../modules/ajax',
  4 |   '../modules/Events',
  5 |   './bindContext',
  6 |   '../query/serverData'
  7 | ], function (blocks, ajax, Events, bindContext, serverData) {
  8 |   /**
  9 |    * @namespace View
 10 |    */
 11 |   function View(application, parentView) {
 12 |     var _this = this;
 13 | 
 14 |     bindContext(this);
 15 |     this._views = [];
 16 |     this._application = application;
 17 |     this._parentView = parentView || null;
 18 |     this._initCalled = false;
 19 |     this._html = undefined;
 20 | 
 21 |     this.loading = blocks.observable(false);
 22 |     this.isActive = blocks.observable(!blocks.has(this.options, 'route'));
 23 |     this.isActive.on('changing', function (oldValue, newValue) {
 24 |       _this._tryInitialize(newValue);
 25 |     });
 26 | 
 27 |     if (this.options.preload || this.isActive()) {
 28 |       this._load();
 29 |     }
 30 |   }
 31 | 
 32 |   View.prototype = {
 33 |     /**
 34 |      * Determines if the view is visible or not.
 35 |      * This property is automatically populated when routing is enabled for the view.
 36 |      *
 37 |      * @memberof View
 38 |      * @name isActive
 39 |      * @type {blocks.observable}
 40 |      */
 41 | 
 42 |     /**
 43 |      * Override the init method to perform actions when the View is first created
 44 |      * and shown on the page
 45 |      *
 46 |      * @memberof View
 47 |      * @type {Function}
 48 |      *
 49 |      * @example {javascript}
 50 |      * var App = blocks.Application();
 51 |      *
 52 |      * App.View('Statistics', {
 53 |      *   init: function () {
 54 |      *     this.loadRemoteData();
 55 |      *   },
 56 |      *
 57 |      *   loadRemoteData: function () {
 58 |      *     // ...stuff...
 59 |      *   }
 60 |      * });
 61 |      */
 62 |     init: blocks.noop,
 63 | 
 64 |     /**
 65 |      * Override the ready method to perform actions when the DOM is ready and
 66 |      * all data-query have been executed.
 67 |      *
 68 |      * @memberof View
 69 |      * @type {Function}
 70 |      *
 71 |      * @example {javascript}
 72 |      * var App = blocks.Application();
 73 |      *
 74 |      * App.View('ContactUs', {
 75 |      *   ready: function () {
 76 |      *     $('#contact-form').ajaxSubmit();
 77 |      *   }
 78 |      * });
 79 |      */
 80 |     ready: blocks.noop,
 81 | 
 82 |     /**
 83 |      * Override the routed method to perform actions when the View have routing and routing
 84 |      * mechanism actives it.
 85 |      *
 86 |      * @memberof View
 87 |      * @type {Function}
 88 |      *
 89 |      * @example {javascript}
 90 |      * var App = blocks.Application();
 91 |      *
 92 |      * App.View('ContactUs', {
 93 |      *   options: {
 94 |      *     route: 'contactus'
 95 |      *   },
 96 |      *
 97 |      *   routed: function () {
 98 |      *     alert('Navigated to ContactUs page!')
 99 |      *   }
100 |      * });
101 |      */
102 |     routed: blocks.noop,
103 | 
104 |     /**
105 |      * Observable which value is true when the View html
106 |      * is being loaded using ajax request. It could be used
107 |      * to show a loading indicator.
108 |      *
109 |      * @memberof View
110 |      */
111 |     loading: blocks.observable(false),
112 | 
113 |     /**
114 |      * Gets the parent view.
115 |      * Returns null if the view is not a child of another view.
116 |      *
117 |      * @memberof View
118 |      */
119 |     parentView: function () {
120 |       return this._parentView;
121 |     },
122 | 
123 |     /**
124 |      * Routes to a specific URL and actives the appropriate views associated with the URL
125 |      *
126 |      * @memberof View
127 |      * @param {String} name -
128 |      * @returns {View} - Chainable. Returns this
129 |      *
130 |      * @example {javascript}
131 |      * var App = blocks.Application();
132 |      *
133 |      * App.View('ContactUs', {
134 |      *   options: {
135 |      *     route: 'contactus'
136 |      *   }
137 |      * });
138 |      *
139 |      * App.View('Navigation', {
140 |      *   navigateToContactUs: function () {
141 |      *     this.route('contactus')
142 |      *   }
143 |      * });
144 |      */
145 |     route: function (/* name */ /*, ...params */) {
146 |       this._application._history.navigate(blocks.toArray(arguments).join('/'));
147 |       return this;
148 |     },
149 | 
150 |     navigateTo: function (view, params) {
151 |       this._application.navigateTo(view, params);
152 |     },
153 | 
154 |     _tryInitialize: function (isActive) {
155 |       if (!this._initialized && isActive) {
156 |         if (this.options.url && !this._html) {
157 |           this._callInit();
158 |           this._load();
159 |         } else {
160 |           this._initialized = true;
161 |           this._callInit();
162 |           if (this.isActive()) {
163 |             this.isActive.update();
164 |           }
165 |         }
166 |       }
167 |     },
168 | 
169 |     _routed: function (params, metadata) {
170 |       this._tryInitialize(true);
171 |       this.routed(params, metadata);
172 |       blocks.each(this._views, function (view) {
173 |         if (!view.options.route) {
174 |           view._routed(params, metadata);
175 |         }
176 |       });
177 |       this.isActive(true);
178 |     },
179 | 
180 |     _callInit: function () {
181 |       if (this._initCalled) {
182 |         return;
183 |       }
184 | 
185 |       var key;
186 |       var value;
187 | 
188 |       blocks.__viewInInitialize__ = this;
189 |       for (key in this) {
190 |         value = this[key];
191 |         if (blocks.isObservable(value)) {
192 |           value.__context__ = this;
193 |         }
194 |       }
195 |       this.init();
196 |       blocks.__viewInInitialize__ = undefined;
197 |       this._initCalled = true;
198 |     },
199 | 
200 |     _load: function () {
201 |       var url = this.options.url;
202 |       if (serverData.hasData && serverData.data.views && serverData.data.views[url]) {
203 |         url = this.options.url = undefined;
204 |         this._tryInitialize(true);
205 |       }
206 | 
207 |       if (url && !this.loading()) {
208 |         this.loading(true);
209 |         ajax({
210 |           isView: true,
211 |           url: url,
212 |           success: blocks.bind(this._loaded, this),
213 |           error: blocks.bind(this._error, this)
214 |         });
215 |       }
216 |     },
217 | 
218 |     _loaded: function (html) {
219 |       this._html = html;
220 |       this._tryInitialize(true);
221 |       this.loading(false);
222 |     },
223 | 
224 |     _error: function () {
225 |       this.loading(false);
226 |     },
227 |     // View is a singleton so return a reference
228 |     clone: function () {
229 |       return this;
230 |     }
231 |   };
232 | 
233 |   Events.register(View.prototype, ['on', 'off', 'trigger']);
234 | 
235 |   /* @if DEBUG */ {
236 |     blocks.debug.addType('View', function (value) {
237 |       if (value && View.prototype.isPrototypeOf(value)) {
238 |         return true;
239 |       }
240 |       return false;
241 |     });
242 |   } /* @endif */
243 |   return View;
244 | });
245 | 


--------------------------------------------------------------------------------
/src/mvc/bindContext.js:
--------------------------------------------------------------------------------
 1 | define([
 2 | 	'../core'
 3 | ], function (blocks) {
 4 | 	function bindContext (context, object) {
 5 | 		var key;
 6 | 		var value;
 7 | 		/* @if DEBUG */ blocks.debug.pause(); /* @endif */
 8 | 		if (!object) {
 9 | 			object = context;
10 | 		}
11 | 
12 | 		for (key in object) {
13 | 			value = object[key];
14 | 
15 | 			if (blocks.isObservable(value)) {
16 | 				context[key].__context__ = context;
17 | 			} else if (blocks.isFunction(value)) {
18 | 				context[key] = blocks.bind(value, context);
19 | 			}
20 | 
21 | 		}
22 | 		/* @if DEBUG */ blocks.debug.resume(); /* @endif */
23 | 	}
24 | 	return bindContext;
25 | });


--------------------------------------------------------------------------------
/src/mvc/clonePrototype.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   './Model',
 3 |   './Property'
 4 | ], function (Model, Property) {
 5 |   function clonePrototype(prototype, object) {
 6 |     var key;
 7 |     var value;
 8 | 
 9 |     for (key in prototype) {
10 |       value = prototype[key];
11 |       if (Property.Is(value)) {
12 |         continue;
13 |       }
14 | 
15 |       if (blocks.isObservable(value)) {
16 |         // clone the observable and also its value by passing true to the clone method
17 |         object[key] = value.clone(true);
18 |         object[key].__context__ = object;
19 |       } else if (blocks.isFunction(value)) {
20 |         object[key] = blocks.bind(value, object);
21 |       } else if (Model.prototype.isPrototypeOf(value)) {
22 |         object[key] = value.clone(true);
23 |       } else if (blocks.isObject(value) && !blocks.isPlainObject(value)) {
24 |         object[key] = blocks.clone(value, true);
25 |       } else {
26 |         object[key] = blocks.clone(value, true);
27 |       }
28 |     }
29 |   }
30 | 
31 |   return clonePrototype;
32 | });
33 | 


--------------------------------------------------------------------------------
/src/mvc/queries.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../query/var/queries',
  4 |   '../query/animation',
  5 |   '../query/addListener',
  6 |   '../query/createVirtual',
  7 |   '../query/VirtualElement',
  8 |   '../query/Expression'
  9 | ], function (blocks, queries, animation, addListener, createVirtual, VirtualElement, Expression) {
 10 |   blocks.extend(blocks.queries, {
 11 |     /**
 12 |      * Associates the element with the particular view and creates a $view context property.
 13 |      * The View will be automatically hidden and shown if the view have routing. The visibility
 14 |      * of the View could be also controlled using the isActive observable property
 15 |      *
 16 |      * @memberof blocks.queries
 17 |      * @param {View} view - The view to associate with the current element
 18 |      *
 19 |      * @example {html}
 20 |      * <!-- Associating the div element and its children with the Profiles view -->
 21 |      * <div data-query="view(Profiles)">
 22 |      *   <!-- looping through the View users collection -->
 23 |      *   <ul data-query="each(users)">
 24 |      *     <!-- Using the $view context value to point to the View selectUser handler -->
 25 |      *     <li data-query="click($view.selectUser)">{{username}}</li>
 26 |      *   </ul>
 27 |      * </div>
 28 |      *
 29 |      * @example {javascript}
 30 |      * var App = blocks.Application();
 31 |      *
 32 |      * App.View('Profiles', {
 33 |      *   users: [{ username: 'John' }, { username: 'Doe' }],
 34 |      *
 35 |      *   selectUser: function (e) {
 36 |      *     // ...stuff...
 37 |      *   }
 38 |      * });
 39 |      */
 40 |     view: {
 41 |       passDomQuery: true,
 42 | 
 43 |       preprocess: function (domQuery, view) {
 44 |         if (!view.isActive()) {
 45 |           this.css('display', 'none');
 46 |         } else {
 47 |           //view._tryInitialize(view.isActive());
 48 |           this.css('display', '');
 49 |           if (view._html) {
 50 |             blocks.queries.template.preprocess.call(this, domQuery, view._html, view);
 51 |           }
 52 |           // Quotes are used because of IE8 and below. It fails with 'Expected idenfitier'
 53 |           //queries['with'].preprocess.call(this, domQuery, view, '$view');
 54 |           //queries.define.preprocess.call(this, domQuery, view._name, view);
 55 |         }
 56 | 
 57 |         queries['with'].preprocess.call(this, domQuery, view, '$view');
 58 |       },
 59 | 
 60 |       update: function (domQuery, view) {
 61 |         if (view.isActive()) {
 62 |           if (view._html) {
 63 |             // Quotes are used because of IE8 and below. It fails with 'Expected idenfitier'
 64 |             queries['with'].preprocess.call(this, domQuery, view, '$view');
 65 | 
 66 |             this.innerHTML = view._html;
 67 |             view._children = view._html = undefined;
 68 |             blocks.each(createVirtual(this.childNodes[0]), function (element) {
 69 |               if (VirtualElement.Is(element)) {
 70 |                 element.sync(domQuery);
 71 |               } else if (element && element.isExpression && element.element) {
 72 |                 element.element.nodeValue = Expression.GetValue(domQuery._context, null, element);
 73 |               }
 74 |             });
 75 |             domQuery.createElementObservableDependencies(this.childNodes);
 76 |           }
 77 |           animation.show(this);
 78 |         } else {
 79 |           animation.hide(this);
 80 |         }
 81 |       }
 82 |     },
 83 | 
 84 |     /**
 85 |      * Navigates to a particular view by specifying the target view or route and optional parameters
 86 |      *
 87 |      * @memberof blocks.queries
 88 |      * @param {(View|String)} viewOrRoute - the view or route to which to navigate to
 89 |      * @param {Object} [params] - parameters needed for the current route
 90 |      *
 91 |      * @example {html}
 92 |      * <!-- will navigate to /contactus because the ContactUs View have /contactus route -->
 93 |      * <a data-query="navigateTo(ContactUs)">Contact Us</a>
 94 |      *
 95 |      * <!-- will navigate to /products/t-shirts because the Products View have /products/{{category}} route -->
 96 |      * <a data-query="navigateTo(Products, { category: 't-shirts' })">T-Shirts</a>
 97 |      *
 98 |      * <!-- the same example as above but the route is directly specifying instead of using the View instance -->
 99 |      * <a data-query="navigateTo('/products/{{category}}', { category: 't-shirts' })">T-Shirts</a>
100 |      */
101 |     navigateTo: {
102 |       update: function (viewOrRoute, params) {
103 |         function navigate(e) {
104 |           e = e || window.event;
105 |           e.preventDefault();
106 |           e.returnValue = false;
107 | 
108 |           if (blocks.isString(viewOrRoute)) {
109 |             window.location.href = viewOrRoute;
110 |           } else {
111 |             viewOrRoute.navigateTo(viewOrRoute, params);
112 |           }
113 |         }
114 | 
115 |         addListener(this, 'click', navigate);
116 |       }
117 |     },
118 | 
119 |     trigger: {
120 | 
121 |     }
122 |   });
123 | });
124 | 


--------------------------------------------------------------------------------
/src/mvc/validation.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../modules/Events',
  4 |   '../query/observable',
  5 |   './validators'
  6 | ], function (blocks, Events, observable, validators) {
  7 |   // TODO: asyncValidate
  8 |   blocks.observable.validation = function (options) {
  9 |     var _this = this;
 10 |     var maxErrors = options.maxErrors;
 11 |     var errorMessages = this.errorMessages = blocks.observable([]);
 12 |     var validatorsArray = this._validators = [];
 13 |     var key;
 14 |     var option;
 15 | 
 16 |     this.errorMessage = blocks.observable('');
 17 | 
 18 |     for (key in options) {
 19 |       option = options[key];
 20 |       if (validators[key]) {
 21 |         validatorsArray.push({
 22 |           option: option,
 23 |           validate: validators[key].validate,
 24 |           priority: validators[key].priority
 25 |         });
 26 |       } else if (key == 'validate' || key == 'asyncValidate') {
 27 |         validatorsArray.push({
 28 |           option: '',
 29 |           validate: option.validate || option,
 30 |           priority: option.priority || Number.POSITIVE_INFINITY,
 31 |           isAsync: key == 'asyncValidate'
 32 |         });
 33 |       }
 34 |     }
 35 | 
 36 |     validatorsArray.sort(function (a, b) {
 37 |       return a.priority > b.priority ? 1 : -1;
 38 |     });
 39 | 
 40 |     this.valid = blocks.observable(true);
 41 | 
 42 |     this.hasValidator = function (validator) {
 43 |       if (!validator) {
 44 |         return validatorsArray.length > 0;
 45 |       }
 46 | 
 47 |       if (blocks.isString(validator)) {
 48 |         validator = validators[validator].validate;
 49 |       }
 50 | 
 51 |       if (!validator) {
 52 |         return false;
 53 |       }
 54 |       for (var i = 0; i < validatorsArray.length; i++) {
 55 |         if (validatorsArray[i].validate == validator) {
 56 |           return true;
 57 |         }
 58 |       }
 59 |       return false;
 60 |     };
 61 | 
 62 |     this.validate = function () {
 63 |       var value = _this._getValue();
 64 |       var isValid = true;
 65 |       var errorsCount = 0;
 66 |       var i = 0;
 67 |       var validationOptions;
 68 |       var validator;
 69 |       var message;
 70 | 
 71 |       errorMessages.removeAll();
 72 |       for (; i < validatorsArray.length; i++) {
 73 |         if (errorsCount >= maxErrors) {
 74 |           break;
 75 |         }
 76 |         validator = validatorsArray[i];
 77 |         if (validator.isAsync) {
 78 |           validator.validate.call(_this.__context__, value, function (result) {
 79 |             validationComplete(_this, options, !!result);
 80 |           });
 81 |           return true;
 82 |         } else {
 83 |           validationOptions = validator.option;
 84 |           option = validator.option;
 85 |           if (blocks.isPlainObject(validationOptions)) {
 86 |             option = validationOptions.value;
 87 |           }
 88 |           if (blocks.isFunction(option)) {
 89 |             option = option.call(_this.__context__);
 90 |           }
 91 |           message = validator.validate.call(_this.__context__, value, options, option);
 92 |           if (blocks.isString(message)) {
 93 |             message = [message];
 94 |           }
 95 |           if (blocks.isArray(message) || !message) {
 96 |             errorMessages.addMany(
 97 |                 blocks.isArray(message) ? message :
 98 |                 validationOptions && validationOptions.message ? [validationOptions.message] :
 99 |                 option && blocks.isString(option) ? [option] :
100 |                 []);
101 |             isValid = false;
102 |             errorsCount++;
103 |           }
104 |         }
105 |       }
106 | 
107 |       validationComplete(this, options, isValid);
108 |       this.valid(isValid);
109 |       Events.trigger(this, 'validate');
110 |       return isValid;
111 |     };
112 | 
113 |     if (options.validateOnChange) {
114 |       this.on('change', function () {
115 |         this.validate();
116 |       });
117 |     }
118 |     if (options.validateInitially) {
119 |       this.validate();
120 |     }
121 |   };
122 | 
123 |   function validationComplete(observable, options, isValid) {
124 |     var errorMessage = observable.errorMessage;
125 |     var errorMessages = observable.errorMessages;
126 | 
127 |     if (isValid) {
128 |       errorMessage('');
129 |     } else {
130 |       errorMessage(options.errorMessage || errorMessages()[0] || '');
131 |     }
132 | 
133 |     observable.valid(isValid);
134 |   }
135 | });
136 | 


--------------------------------------------------------------------------------
/src/mvc/validators.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core'
  3 | ], function (blocks) {
  4 |   var validators = {
  5 |     required: {
  6 |       priority: 9,
  7 |       validate: function (value, options) {
  8 |         if (value !== options.defaultValue &&
  9 |             value !== '' &&
 10 |             value !== false &&
 11 |             value !== undefined &&
 12 |             value !== null) {
 13 |           return true;
 14 |         }
 15 |       }
 16 |     },
 17 | 
 18 |     minlength: {
 19 |       priority: 19,
 20 |       validate: function (value, options, option) {
 21 |         if (value === undefined || value === null) {
 22 |           return false;
 23 |         }
 24 |         return value.length >= parseInt(option, 10);
 25 |       }
 26 |     },
 27 | 
 28 |     maxlength: {
 29 |       priority: 29,
 30 |       validate: function (value, options, option) {
 31 |         if (value === undefined || value === null) {
 32 |           return true;
 33 |         }
 34 |         return value.length <= parseInt(option, 10);
 35 |       }
 36 |     },
 37 | 
 38 |     min: {
 39 |       priority: 39,
 40 |       validate: function (value, options, option) {
 41 |         if (value === undefined || value === null) {
 42 |           return false;
 43 |         }
 44 |         return value >= option;
 45 |       }
 46 |     },
 47 | 
 48 |     max: {
 49 |       priority: 49,
 50 |       validate: function (value, options, option) {
 51 |         if (value === undefined || value === null) {
 52 |           return false;
 53 |         }
 54 |         return value <= option;
 55 |       }
 56 |     },
 57 | 
 58 |     email: {
 59 |       priority: 59,
 60 |       validate: function (value) {
 61 |         return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value);
 62 |       }
 63 |     },
 64 | 
 65 |     url: {
 66 |       priority: 69,
 67 |       validate: function (value) {
 68 |         return /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/.test(value);
 69 |       }
 70 |     },
 71 | 
 72 |     date: {
 73 |       priority: 79,
 74 |       validate: function (value) {
 75 |         if (!value) {
 76 |           return false;
 77 |         }
 78 |         return !/Invalid|NaN/.test(new Date(value.toString()).toString());
 79 |       }
 80 |     },
 81 | 
 82 |     creditcard: {
 83 |       priority: 89,
 84 |       validate: function (value) {
 85 |         if (blocks.isString(value) && value.length === 0) {
 86 |           return false;
 87 |         }
 88 |         if (blocks.isNumber(value)) {
 89 |           value = value.toString();
 90 |         }
 91 |         // accept only spaces, digits and dashes
 92 |         if (/[^0-9 \-]+/.test(value)) {
 93 |           return false;
 94 |         }
 95 |         var nCheck = 0,
 96 |             nDigit = 0,
 97 |             bEven = false;
 98 | 
 99 |         value = value.replace(/\D/g, '');
100 | 
101 |         for (var n = value.length - 1; n >= 0; n--) {
102 |           var cDigit = value.charAt(n);
103 |           nDigit = parseInt(cDigit, 10);
104 |           if (bEven) {
105 |             if ((nDigit *= 2) > 9) {
106 |               nDigit -= 9;
107 |             }
108 |           }
109 |           nCheck += nDigit;
110 |           bEven = !bEven;
111 |         }
112 | 
113 |         return (nCheck % 10) === 0;
114 |       }
115 |     },
116 | 
117 |     regexp: {
118 |       priority: 99,
119 |       validate: function (value, options, option) {
120 |         if (!blocks.isRegExp(option)) {
121 |           return false;
122 |         }
123 |         if (value === undefined || value === null) {
124 |           return false;
125 |         }
126 |         return option.test(value);
127 |       }
128 |     },
129 | 
130 |     number: {
131 |       priority: 109,
132 |       validate: function (value) {
133 |         if (blocks.isNumber(value)) {
134 |           return true;
135 |         }
136 |         if (blocks.isString(value) && value.length === 0) {
137 |           return false;
138 |         }
139 |         return /^(-?|\+?)(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
140 |       }
141 |     },
142 | 
143 |     digits: {
144 |       priority: 119,
145 |       validate: function (value) {
146 |         return /^\d+$/.test(value);
147 |       }
148 |     },
149 | 
150 |     letters: {
151 |       priority: 129,
152 |       validate: function (value) {
153 |         if (!value) {
154 |           return false;
155 |         }
156 |         return /^[a-zA-Z]+$/.test(value);
157 |       }
158 |     },
159 | 
160 |     equals: {
161 |       priority: 139,
162 |       validate: function (value, options, option) {
163 |         return blocks.equals(value, blocks.unwrap(option));
164 |       }
165 |     }
166 |   };
167 | 
168 |   return validators;
169 | });
170 | 


--------------------------------------------------------------------------------
/src/mvc/var/COLLECTION.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 | 	return '__blocks.Application.Collection__';
3 | });


--------------------------------------------------------------------------------
/src/mvc/var/MODEL.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 | 	return '__blocks.Application.Model__';
3 | });


--------------------------------------------------------------------------------
/src/node.js:
--------------------------------------------------------------------------------
1 | define([
2 |   './mvc',
3 |   './node/methods',
4 |   './node/overrides'
5 | ], function () {
6 | 
7 | });


--------------------------------------------------------------------------------
/src/node/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 |   "browser": false,
3 | 
4 |   "globals": {
5 |     "require": true,
6 |     "server": true
7 |   }
8 | }
9 | 


--------------------------------------------------------------------------------
/src/node/BrowserEnv.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   './createBrowserEnvObject',
 3 |   '../core'
 4 | ], function (createBrowserEnvObject, blocks) {
 5 |   var url = require('url');
 6 | 
 7 |   function BrowserEnv() {
 8 |     var env = createBrowserEnvObject();
 9 |     this._env = env;
10 | 
11 |     this._initialize();
12 |   }
13 | 
14 |   BrowserEnv.Create = function () {
15 |     return new BrowserEnv();
16 |   };
17 | 
18 |   BrowserEnv.prototype = {
19 |     getObject: function () {
20 |       return this._env;
21 |     },
22 | 
23 |     fillLocation: function (fullUrl) {
24 |       var props = url.parse(fullUrl);
25 |       var copy = 'host hostname href pathname protocol'.split(' ');
26 |       var location = this._env.window.location;
27 | 
28 |       blocks.each(copy, function (name) {
29 |         location[name] = props[name];
30 |       });
31 |     },
32 |     setBaseUrl: function (url) {
33 |       if (url) {
34 |         this._env.__baseUrl__ = url;
35 |       }
36 |     },
37 |     addElementsById: function (elementsById) {
38 |       var env = this._env;
39 |       env.document.__elementsById__ = elementsById;
40 |       blocks.each(elementsById, function (element, id) {
41 |         env[id] = element;
42 |       });
43 |     },
44 | 
45 |     fillServer: function (server) {
46 |       var window = this._env.window;
47 |       var timeout = setTimeout;
48 |       var clear = clearTimeout;
49 | 
50 |       window.setTimeout = function (callback, delay) {
51 |         if (delay === 0) {
52 |           server.wait();
53 |           return timeout(function () {
54 |             var result = callback();
55 | 
56 |             server.ready();
57 | 
58 |             return result;
59 |           }, delay);
60 |         }
61 | 
62 |         return timeout(blocks.noop, delay);
63 |       };
64 | 
65 |       window.clearTimeout = function (id) {
66 |         return clear(id);
67 |       };
68 |     },
69 | 
70 |     _initialize: function () {
71 |       var env = this._env;
72 |       var document = env.document;
73 |       env.window._blocks = blocks;
74 | 
75 |       document.getElementById = function (id) {
76 |         return (document.__elementsById__ || {})[id] || null;
77 |       };
78 |     }
79 |   };
80 |   return BrowserEnv;
81 | });


--------------------------------------------------------------------------------
/src/node/Middleware.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   './findPageScripts',
  4 |   './executePageScripts',
  5 |   './getElementsById',
  6 |   './parseToVirtual',
  7 |   './ServerEnv',
  8 |   './BrowserEnv',
  9 |   '../query/VirtualElement'
 10 | ], function (blocks, findPageScripts, executePageScripts, getElementsById, parseToVirtual, ServerEnv, BrowserEnv, VirtualElement) {
 11 |   var fs = require('fs');
 12 |   var path = require('path');
 13 | 
 14 |   function Middleware(options) {
 15 |     if (blocks.isString(options)) {
 16 |       options = {
 17 |         static: options
 18 |       };
 19 |     }
 20 | 
 21 |     this._options = blocks.extend({}, Middleware.Defaults, options);
 22 |     this._contents = '';
 23 |     this._scripts = [];
 24 |     this._cache = {};
 25 |     this._initialized = false;
 26 | 
 27 |     this._initialize();
 28 |   }
 29 | 
 30 |   Middleware.Defaults = {
 31 |     static: 'app',
 32 |     cache: true,
 33 |     baseTag: false
 34 |   };
 35 | 
 36 |   Middleware.prototype = {
 37 |     tryServePage: function (req, res, next) {
 38 |       this._renderContents(req, function (err, contents) {
 39 |         if (err && (err != 'no query' || req.url != '/')) {
 40 |           next();
 41 |         } else {
 42 |           res.send(contents);
 43 |           return;
 44 |         }
 45 |       });
 46 |     },
 47 | 
 48 |     _initialize: function () {
 49 |       var _this = this;
 50 |       var url = path.join(this._options.static, '/index.html');
 51 | 
 52 |       fs.readFile(url, { encoding: 'utf-8' }, function (err, contents) {
 53 |         if (!err) {
 54 |           _this._setContents(contents);
 55 |         }
 56 |       });
 57 |     },
 58 | 
 59 |     _setContents: function (contents) {
 60 |       var _this = this;
 61 |       var virtual = blocks.first(parseToVirtual(contents), function (child) {
 62 |         return VirtualElement.Is(child);
 63 |       });
 64 | 
 65 |       this._contents = contents;
 66 |       this._elementsById = getElementsById(virtual.children());
 67 | 
 68 |       findPageScripts(virtual, this._options.static, function (scripts) {
 69 |         _this._scripts = scripts;
 70 |         _this._initialized = true;
 71 |       });
 72 |     },
 73 | 
 74 |     _renderContents: function (req, callback) {
 75 |       var cache = this._cache;
 76 |       var location = this._getLocation(req);
 77 |       var env;
 78 | 
 79 |       if (!this._initialized) {
 80 |         callback('not initialized', null);
 81 |       } else if (this._options.cache && cache[location]) {
 82 |         callback(null, cache[location]);
 83 |       } else {
 84 |         env = this._createEnv(req);
 85 |         executePageScripts(env, this._scripts, this._pageExecuted.bind(this, callback, env.location.href));
 86 |       }
 87 |     },
 88 | 
 89 |     _pageExecuted: function (callback, location, err, contents) {
 90 |       if (!err && this._options.cache) {
 91 |         this._cache[location] = contents;
 92 |       }
 93 |       callback(err, contents);
 94 |     },
 95 | 
 96 |     _createEnv: function (req) {
 97 |       var server = new ServerEnv(this._options, this._contents);
 98 | 
 99 |       return blocks.extend({ server: server }, this._createBrowserEnv(req, server));
100 |     },
101 | 
102 |     _createBrowserEnv: function (req, server) {
103 |       var browserEnv = BrowserEnv.Create();
104 | 
105 |       browserEnv.setBaseUrl(req.baseUrl);
106 |       browserEnv.fillLocation(this._getLocation(req));
107 |       browserEnv.addElementsById(this._elementsById);
108 |       browserEnv.fillServer(server);
109 | 
110 |       return browserEnv.getObject();
111 |     },
112 | 
113 |     _getLocation: function (req) {
114 |       return req.protocol + '://' + req.get('host') + req.originalUrl;
115 |     }
116 |   };
117 | 
118 |   return Middleware;
119 | });


--------------------------------------------------------------------------------
/src/node/Server.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   './Middleware'
 4 | ], function (blocks, Middleware) {
 5 |   var path = require('path');
 6 |   var express = require('express');
 7 | 
 8 |   function Server(options) {
 9 |     this._options = blocks.extend({}, Server.Defaults, options);
10 |     this._app = express();
11 |     this._middleware = new Middleware(options);
12 | 
13 |     this._init();
14 |   }
15 | 
16 |   Server.Defaults = blocks.extend({}, Middleware.Defaults, {
17 |     port: 8000,
18 |     use: null
19 |   });
20 | 
21 |   Server.prototype = {
22 |     _started: false,
23 |     /**
24 |      * Returns the express instance used by the blocks server internally.
25 |      * When called synchronous directly after the server has been crated
26 |      * pre middlewares can be added.
27 |      * But the server will need to be started explicitly.
28 |      * @return {object} the express app
29 |      * @example {javascript}
30 |      * var server = blocks.server();
31 |      * var app = blocks.express();
32 |      * app.use('/test', function (req, res) {
33 |      *   res.json({some: 'test'});
34 |      * });
35 |      * // required for ssr to work now
36 |      * server.start();
37 |      */
38 |     express: function () {
39 |       this._delayStart = true;
40 |       return this._app;
41 |     },
42 | 
43 |     _init: function () {
44 |       var options = this._options;
45 |       var app = this._app;
46 |       var self = this;
47 | 
48 |       app.listen(options.port);
49 | 
50 |       blocks.each(blocks.toArray(options.use), function (middleware) {
51 |         if (blocks.isFunction(middleware)) {
52 |           app.use(middleware);
53 |         }
54 |       });
55 | 
56 |       app.use(express.static(path.resolve(options.static), {
57 |         index: false
58 |       }));
59 | 
60 |       process.nextTick(function () {
61 |         if (!self._delayStart) {
62 |           self.start();
63 |         }
64 |       });
65 | 
66 |     },
67 | 
68 |     /**
69 |      * Starts the ssr server.
70 |      * Does not need to be called if server.express()
71 |      * is not called.
72 |      */
73 |     start: function () {
74 |       if (this._started) {
75 |         return;
76 |       }
77 |       var middleware = this._middleware;
78 |       this._app.get('/*', function (req, res, next) {
79 |         middleware.tryServePage(req, res, next);
80 |       });
81 |       this._started = true;
82 |     },
83 | 
84 |     /**
85 |      * Returns if the server has been started.
86 |      * @return {boolean}
87 |      */
88 |     started: function () {
89 |       return this._started;
90 |     }
91 |   };
92 |   return Server;
93 | });


--------------------------------------------------------------------------------
/src/node/ServerEnv.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   '../modules/Events'
 4 | ], function (blocks, Events) {
 5 |   function ServerEnv(options, html) {
 6 |     this.options = options;
 7 |     this.html = html;
 8 |     this.data = {};
 9 |     this._root = this._createBubbleNode();
10 |     this._node = this._root;
11 | 
12 |     this.on('ready', blocks.bind(this._ready, this));
13 |   }
14 | 
15 |   ServerEnv.prototype = {
16 |     _waiting: 0,
17 | 
18 |     rendered: '',
19 | 
20 |     isReady: function () {
21 |       return this._waiting === 0;
22 |     },
23 | 
24 |     wait: function () {
25 |       this._waiting += 1;
26 |     },
27 | 
28 |     ready: function () {
29 |       if (this._waiting > 0) {
30 |         this._waiting -= 1;
31 |         if (this._waiting === 0) {
32 |           this.trigger('ready');
33 |         }
34 |       }
35 |     },
36 | 
37 |     await: function (callback) {
38 |       if (this.isReady()) {
39 |         callback();
40 |       } else {
41 |         this._createBubbleNode(this._node, callback);
42 |       }
43 |     },
44 | 
45 |     _ready: function () {
46 |       var node = this._node;
47 | 
48 |       while (this.isReady()) {
49 |         if (!node.isRoot) {
50 |           node.callback();
51 |         }
52 |         this._node = node = this._next(node);
53 | 
54 |         if (node === this._root) {
55 |           break;
56 |         }
57 |       }
58 |     },
59 | 
60 |     _next: function (node) {
61 |       var parent = node;
62 |       var next;
63 | 
64 |       while (!next && parent) {
65 |         next = parent.nodes.pop();
66 |         parent = parent.parent;
67 |       }
68 | 
69 |       return next || this._root;
70 |     },
71 | 
72 |     _createBubbleNode: function (parent, callback) {
73 |       var node = {
74 |         isRoot: !parent,
75 |         parent: parent,
76 |         callback: callback,
77 |         nodes: []
78 |       };
79 | 
80 |       if (parent) {
81 |         parent.nodes.unshift(node);
82 |       }
83 | 
84 |       return node;
85 |     }
86 |   };
87 | 
88 |   Events.register(ServerEnv.prototype, ['on', 'once', 'off', 'trigger']);
89 | 
90 |   return ServerEnv;
91 | });


--------------------------------------------------------------------------------
/src/node/createBrowserEnvObject.js:
--------------------------------------------------------------------------------
  1 | define(function () {
  2 |   function createBrowserEnvObject() {
  3 |     var windowObj = createWindowContext();
  4 | 
  5 |     return blocks.extend(windowObj, {
  6 |       document: createDocumentContext()
  7 |     });
  8 |   }
  9 | 
 10 |   function createDocumentContext() {
 11 |     var base = createElementMock();
 12 | 
 13 |     return blocks.extend(base, {
 14 |       __mock__: true,
 15 | 
 16 |       doctype: createElementMock(),
 17 | 
 18 |       body: createElementMock(),
 19 | 
 20 |       createElement: function (tagName) {
 21 |         return createElementMock(tagName);
 22 |       },
 23 | 
 24 |       getElementById: function () {
 25 |         return null;
 26 |       }
 27 |     });
 28 |   }
 29 | 
 30 |   function createWindowContext() {
 31 |     var timeoutId = 0;
 32 |     var windowObj = {
 33 |       __mock__: true,
 34 |       __baseUrl__: '',
 35 | 
 36 |       console: createConsoleMock(),
 37 | 
 38 |       history: {
 39 |         length: 1,
 40 |         state: null,
 41 |         back: blocks.noop,
 42 |         forward: blocks.noop,
 43 |         go: blocks.noop,
 44 |         pushState: blocks.noop,
 45 |         replaceState: blocks.noop
 46 |       },
 47 | 
 48 |       location: {
 49 |         ancestorOrigins: [],
 50 |         assign: blocks.noop,
 51 |         hash: '',
 52 |         host: '',
 53 |         hostname: '',
 54 |         href: '',
 55 |         origin: '',
 56 |         pathname: '',
 57 |         protocol: '',
 58 |         reload: blocks.noop,
 59 |         replace: blocks.noop,
 60 |         search: ''
 61 |       },
 62 | 
 63 |       navigator: {
 64 |         appCodeName: 'Mozilla',
 65 |         appName: 'Netscape',
 66 |         appVersion: '5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36',
 67 |         cookieEnabled: true,
 68 |         doNotTrack: null,
 69 |         //geolocation: {},
 70 |         hardwareConcurrency: 8,
 71 |         language: 'en-us',
 72 |         languages: ['en-US', 'en'],
 73 |         maxTouchPoints: 0,
 74 |         mimeTypes: [],
 75 |         onLine: true,
 76 |         platform: 'Win32',
 77 |         plugins: [],
 78 |         product: 'Gecko',
 79 |         productSub: '20030107',
 80 |         serviceWorker: {},
 81 |         userAgent: 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36',
 82 |         vendor: 'Google Inc.',
 83 |         vendorSub: ''
 84 |         // webkitPersistentStorage: {},
 85 |         // webkitTemporaryStorage: {}
 86 |       },
 87 | 
 88 |       addEventListener: blocks.noop,
 89 |       removeEventListener: blocks.noop,
 90 | 
 91 |       setTimeout: function () {
 92 |         timeoutId += 1;
 93 |         return timeoutId;
 94 |       },
 95 |       clearTimeout: blocks.noop,
 96 | 
 97 |       setInterval: function () {
 98 |         timeoutId += 1;
 99 |         return timeoutId;
100 |       },
101 |       clearInterval: blocks.noop
102 |     };
103 | 
104 |     windowObj.window = windowObj;
105 | 
106 |     return windowObj;
107 |   }
108 | 
109 |   function createElementMock(tagName) {
110 |     return {
111 |       accessKey: '',
112 |       tagName: String(tagName).toLowerCase(),
113 |       getElementsByClassName: function () {
114 |         return [];
115 |       },
116 |       getElementsByTagName: function () {
117 |         return [];
118 |       },
119 |       addEventListener: blocks.noop,
120 |       removeEventListener: blocks.noop,
121 |       contains: function () {
122 |         return true;
123 |       }
124 |     };
125 |   }
126 | 
127 |   function createConsoleMock() {
128 |     return {
129 |       log: blocks.noop
130 |     };
131 |   }
132 | 
133 |   return createBrowserEnvObject;
134 | });


--------------------------------------------------------------------------------
/src/node/executePageScripts.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../query/ElementsData'
  4 | ], function (blocks, ElementsData) {
  5 |  // var vm = require('vm');
  6 | 
  7 |   function executePageScripts(env, scripts, callback) {
  8 |     var code = 'with(window) {';
  9 | 
 10 |     blocks.each(scripts, function (script) {
 11 |       code += script.code + ';';
 12 |     });
 13 | 
 14 |     code += '}';
 15 |     executeCode(env, code, callback);
 16 |   }
 17 | 
 18 |   var funcs = {};
 19 |   function executeCode(env, code, callback) {
 20 |     contextBubble(env, function () {
 21 |       blocks.core.deleteApplication();
 22 |       ElementsData.reset();
 23 | 
 24 |       if (!funcs[code]) {
 25 |         // jshint -W054
 26 |         // Disable JSHint error: The Function constructor is a form of eval
 27 |         funcs[code] = new Function('blocks', 'document', 'window', 'require', code);
 28 |       }
 29 | 
 30 |       funcs[code].call(this, blocks, env.document, env.window, require);
 31 | 
 32 |       server.await(function () {
 33 |         handleResult(env, callback);
 34 |       });
 35 |     });
 36 |   }
 37 | 
 38 |   function contextBubble(obj, callback) {
 39 |     var _this = this;
 40 |     var values = {};
 41 | 
 42 |     blocks.each(obj, function (val, name) {
 43 |       values[name] = _this[name];
 44 |       _this[name] = val;
 45 |     });
 46 | 
 47 |     callback();
 48 | 
 49 |     server.await(function () {
 50 |       blocks.each(obj, function (val, name) {
 51 |         _this[name] = values[name];
 52 |       });
 53 |     }, true);
 54 |   }
 55 | 
 56 |   function handleResult(env, callback) {
 57 |     var hasRoute = false;
 58 |     var hasActive = false;
 59 |     var application = env.server.application;
 60 |     if (application) {
 61 |       application._createViews();
 62 |       application._startHistory();
 63 | 
 64 |       server.await(function () {
 65 |         blocks.query(application);
 66 |         blocks.each(application._views, function (view) {
 67 |           if (blocks.has(view.options, 'route')) {
 68 |             hasRoute = true;
 69 |           }
 70 |           if (view.isActive()) {
 71 |             hasActive = true;
 72 |           }
 73 |         });
 74 |       });
 75 |     }
 76 | 
 77 |     server.await(function () {
 78 |       if (hasRoute && !hasActive) {
 79 |         callback('not found', null);
 80 |       }
 81 | 
 82 |       if (env.server.rendered) {
 83 |         callback(null, env.server.rendered);
 84 |       } else {
 85 |         callback('no query', env.server.html);
 86 |       }
 87 |     });
 88 |   }
 89 | 
 90 | 
 91 | //  function executeCode(browserEnv, html, code) {
 92 | //    var context = vm.createContext(browserEnv.getObject());
 93 | //    var script = vm.createScript(code);
 94 | //  
 95 | //    blocks.extend(context, {
 96 | //      server: {
 97 | //        html: html,
 98 | //        data: {},
 99 | //        rendered: '',
100 | //        applications: []
101 | //      },
102 | //      require: require
103 | //    });
104 | //  
105 | //    script.runInContext(context);
106 | //  
107 | //    blocks.each(context.server.applications, function (application) {
108 | //      application.start();
109 | //    });
110 | //  
111 | //    return context.server.rendered || html;
112 | //  }
113 | 
114 |   return executePageScripts;
115 | });


--------------------------------------------------------------------------------
/src/node/findPageScripts.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   '../query/VirtualElement'
 4 | ], function (blocks, VirtualElement) {
 5 |   var path = require('path');
 6 |   var fs = require('fs');
 7 | 
 8 |   function findPageScripts(virtual, static, callback) {
 9 |     var scripts = [];
10 |     var args = {
11 |       filesPending: 0,
12 |       callback: callback,
13 |       static: static
14 |     };
15 |     findPageScriptsRecurse(virtual, scripts, args);
16 |     if (args.filesPending === 0) {
17 |       args.callback([]);
18 |     }
19 |   }
20 | 
21 |   function findPageScriptsRecurse(virtual, scripts, args) {
22 |     blocks.each(virtual.children(), function (child) {
23 |       if (!VirtualElement.Is(child)) {
24 |         return;
25 |       }
26 |       var src;
27 | 
28 |       if (child.tagName() == 'script' && (!child.attr('type') || child.attr('type') == 'text/javascript')) {
29 |         src = child.attr('src');
30 |         if (src && !child.attr('data-client-only')) {
31 |           src = path.join(args.static, src);
32 | 
33 |           scripts.push({
34 |             type: 'external',
35 |             url: src,
36 |             code: ''
37 |           });
38 | 
39 |           args.filesPending += 1;
40 |           populateScript(scripts[scripts.length - 1], function () {
41 |             args.filesPending -= 1;
42 |             if (args.filesPending === 0) {
43 |               args.callback(scripts);
44 |             }
45 |           });
46 |         } else {
47 |           scripts.push({
48 |             type: 'page',
49 |             code: child.renderChildren()
50 |           });
51 |         }
52 |       }
53 |       findPageScriptsRecurse(child, scripts, args);
54 |     });
55 |   }
56 | 
57 |   function populateScript(script, callback) {
58 |     fs.readFile(script.url, { encoding: 'utf-8' }, function (err, code) {
59 |       script.code = code;
60 |       callback();
61 |     });
62 |   }
63 | 
64 |   return findPageScripts;
65 | });
66 | 


--------------------------------------------------------------------------------
/src/node/getElementsById.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   '../query/VirtualElement'
 4 | ], function (blocks, VirtualElement) {
 5 |   function getElementsById(elements, result) {
 6 |     result = result || {};
 7 | 
 8 |     blocks.each(elements, function (child) {
 9 |       if (VirtualElement.Is(child)) {
10 |         if (child.attr('id')) {
11 |           result[child.attr('id')] = child;
12 |         }
13 |         getElementsById(child.children(), result);
14 |       }
15 |     });
16 | 
17 |     return result;
18 |   }
19 | 
20 |   return getElementsById;
21 | });


--------------------------------------------------------------------------------
/src/node/methods.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   './Server',
 4 |   './Middleware'
 5 | ], function (blocks, Server, Middleware) {
 6 |   blocks.server = function (options) {
 7 |     return new Server(options);
 8 |   };
 9 | 
10 |   blocks.static = function (options) {
11 | 
12 |   };
13 | 
14 |   blocks.middleware = function (options) {
15 | 	var express = require('express');
16 | 	var path = require('path');
17 | 	// the real middleware
18 | 	var middleware;
19 | 	// array of middlewares to return
20 | 	var middlewares = [];
21 | 
22 | 	var defaults = {
23 | 		// Should the files in static get delivered 
24 | 		deliverStatics: true,
25 | 		baseTag: true
26 | 	};
27 | 
28 | 	if (blocks.isString(options)) {
29 | 		options = {
30 | 			static: options
31 | 		};
32 | 	}
33 | 
34 | 	options = blocks.extend({}, Middleware.Defaults, defaults, options);
35 | 	middleware = new Middleware(options);
36 | 
37 | 	if (options.deliverStatics) {
38 | 		// express.static is required as the frontend app needs it's files
39 | 		middlewares.push(express.static(path.resolve(options.static), {
40 | 			index: false
41 | 		}));
42 | 	}
43 | 
44 | 	middlewares.push(blocks.bind(middleware.tryServePage, middleware));
45 | 
46 | 	// returning array of express middlewares that express will call in that order
47 | 	return middlewares;
48 |   };
49 | });


--------------------------------------------------------------------------------
/src/node/overrides.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../modules/Request',
  4 |   '../query/var/dataIdAttr',
  5 |   '../query/DomQuery',
  6 |   '../query/VirtualElement',
  7 |   '../mvc/Application',
  8 |   './parseToVirtual',
  9 |   '../modules/Escape'
 10 | ], function (blocks, Request, dataIdAttr, DomQuery, VirtualElement, Application, parseToVirtual, Escape) {
 11 |   var eachQuery = blocks.queries.each.preprocess;
 12 | 
 13 |   blocks.queries.each.preprocess = function (domQuery, collection) {
 14 |     if (!server.data[this._attributes[dataIdAttr]]) {
 15 |       removeDataIds(this);
 16 |       server.data[this._attributes[dataIdAttr]] = this.renderChildren();
 17 |     }
 18 | 
 19 |     eachQuery.call(this, domQuery, collection);
 20 |   };
 21 | 
 22 |   function removeDataIds(element) {
 23 |     var children = element._template || element._children;
 24 |     blocks.each(children, function (child) {
 25 |       if (VirtualElement.Is(child)) {
 26 |         child._attributes['data-id'] = null;
 27 |         removeDataIds(child);
 28 |       }
 29 |     });
 30 |   }
 31 | 
 32 |   blocks.query = function (model) {
 33 |     var domQuery = new DomQuery(model);
 34 |     var children = parseToVirtual(server.html);
 35 | 
 36 |     domQuery.pushContext(model);
 37 | 
 38 |     renderChildren(children, domQuery);
 39 |   };
 40 | 
 41 |   function renderChildren(children, domQuery) {
 42 |     var body = findByTagName(children, 'body');
 43 |     var head = findByTagName(children, 'head');
 44 |     var root = VirtualElement();
 45 | 
 46 |     root._children = children;
 47 |     body._parent = null;
 48 |     body.render(domQuery);
 49 | 
 50 |     server.await(function () {
 51 |       if (head) {
 52 |         if (server.options.baseTag) {
 53 |           head.children().splice(0, 0, getBaseTag());
 54 |         }
 55 |       }
 56 |       body.attr('data-blocks-server-data', JSON.stringify(server.data));
 57 |       server.rendered = root.renderChildren();
 58 |     });
 59 |   }
 60 | 
 61 |   function findByTagName(children, tagName) {
 62 |     var result;
 63 | 
 64 |     blocks.each(children, function(child) {
 65 |       if (VirtualElement.Is(child)) {
 66 |         if (child.tagName() == tagName) {
 67 |           result = child;
 68 |           return false;
 69 |         } else {
 70 |           result = findByTagName(child.children(), tagName);
 71 |         }
 72 |       }
 73 |     });
 74 | 
 75 |     return result;
 76 |   }
 77 | 
 78 |   function getBaseTag() {
 79 |     var baseUrl = window.location.protocol + '//' + window.location.host +  window.__baseUrl__ + '/';
 80 |     return VirtualElement('base').attr('href', baseUrl);
 81 |   }
 82 | 
 83 |   var executeExpressionValue = Expression.Execute;
 84 |   var commentRegEx = /^<!-- ([0-9]+):/;
 85 | 
 86 |   Expression.Execute = function (context, elementData, expressionData, entireExpression) {
 87 |     var value = executeExpressionValue(context, elementData, expressionData, entireExpression);
 88 |     var regExResult = commentRegEx.exec(value);
 89 | 
 90 |     if (regExResult) {
 91 |       elementData = ElementsData.byId(regExResult[1]);
 92 |     }
 93 | 
 94 |     if (elementData) {
 95 |       if (expressionData.attributeName) {
 96 |         server.data[elementData.id + expressionData.attributeName] =  entireExpression.text;
 97 |       } else {
 98 |         server.data[elementData.id] = '{{' + expressionData.expression + '}}';
 99 |       }
100 |     }
101 | 
102 |     return value;
103 |   };
104 | 
105 |   Application.prototype._prepare = function () {
106 |     server.application = this;
107 |   };
108 | 
109 |   Application.prototype._viewsReady = blocks.noop;
110 | 
111 |   var viewQuery = blocks.queries.view.preprocess;
112 | 
113 |   blocks.queries.view.preprocess = function (domQuery, view) {
114 |     viewQuery.call(this, domQuery, view);
115 |     if (view._html && server.application && server.application.options.history == 'pushState') {
116 |       this._children = parseToVirtual(view._html);
117 |     }
118 |   };
119 | 
120 |   blocks.queries.template.preprocess = function (domQuery, html, value) {
121 |     if (blocks.isObject(html)) {
122 |       if (VirtualElement.Is(html.rawValue)) {
123 |         html = html.rawValue.html();
124 |       } else {
125 |         html =  GLOBAL[html.rawValue] && GLOBAL[html.rawValue].html();
126 |       }
127 |     }
128 | 
129 |     if (blocks.isObject(value) && value.parameterName) {
130 |       value = value.rawValue;
131 |     }
132 | 
133 |     if (html) {
134 |       if (value) {
135 |         blocks.queries['with'].preprocess.call(this, domQuery, value, '$template');
136 |       }
137 |       this.html(html);
138 |       if (!this._each) {
139 |         this._children = parseToVirtual(this.html());
140 |         this._innerHTML = null;
141 |       }
142 |       server.data.templates = server.data.templates || {};
143 |       server.data.templates[ElementsData.id(this)] = true;
144 |     }
145 |   };
146 | 
147 |   blocks.observable.fn.array.reset = function (array) {
148 |     this.removeAll();
149 | 
150 |     if (arguments.length > 0) {
151 |       this.addMany(blocks.unwrap(array));
152 |     }
153 | 
154 |     return this;
155 |   };
156 | 
157 |   var http = require('http');
158 |   var fs = require('fs');
159 |   var path = require('path');
160 |   Request.prototype.execute = function () {
161 |     var _this = this;
162 |     var options = this.options;
163 |     var url = options.url;
164 |     var relativeUrl;
165 |     var requests;
166 |     var views;
167 | 
168 |     if (options.type == 'GET' && options.data) {
169 |       this.appendDataToUrl(options.data);
170 |     }
171 | 
172 |     if (blocks.startsWith(url, 'http') || blocks.startsWith(url, 'www')) {
173 |       // TODO implement
174 |     } else {
175 |       relativeUrl = path.join(server.options.static, url);
176 |       if (this.options.isView) {
177 |         views = server.data.views = server.data.views || {};
178 |         views[url] = true;
179 |         this.callSuccess(fs.readFileSync(relativeUrl, {encoding: 'utf-8'}));
180 |       } else if (this.options.async === false) {
181 | 
182 |       } else {
183 | 
184 |         server.wait();
185 |         fs.readFile(relativeUrl, { encoding: 'utf-8' }, function (err, contents) {
186 |           requests = server.data.requests = server.data.requests || {};
187 |           requests[url] = contents;
188 |           if (err) {
189 |             _this.callError(err);
190 |           } else {
191 |             _this.callSuccess(contents);
192 |           }
193 |           server.ready();
194 |         });
195 |       }
196 |     }
197 |   };
198 | 
199 |   Request.prototype._handleFileCallback = function (err, contents) {
200 |     if (err) {
201 |       this.callError(err);
202 |     } else {
203 |       this.callSuccess(contents);
204 |     }
205 |   };
206 | 
207 |   var blocksApplication = blocks.Application;
208 | 
209 |   blocks.Application = function (options) {
210 |     var app = blocksApplication(options);
211 |     app._router._setBaseUrl(window.__baseUrl__);
212 |     server.data.baseUrl = window.__baseUrl__;
213 |     return app;
214 |   };
215 | 
216 | });


--------------------------------------------------------------------------------
/src/node/parseToVirtual.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../query/VirtualElement',
  3 |   '../query/VirtualComment',
  4 |   '../query/Expression',
  5 |   '../var/trimRegExp',
  6 |   '../query/var/dataQueryAttr'
  7 | ], function (VirtualElement, VirtualComment, Expression, trimRegExp, dataQueryAttr) {
  8 |   var parse5 = require('parse5');
  9 | 
 10 |   var selfClosingTags = {
 11 |     area: true,
 12 |     base: true,
 13 |     br: true,
 14 |     col: true,
 15 |     embed: true,
 16 |     hr: true,
 17 |     img: true,
 18 |     input: true,
 19 |     keygen: true,
 20 |     link: true,
 21 |     menuitem: true,
 22 |     meta: true,
 23 |     param: true,
 24 |     source: true,
 25 |     track: true,
 26 |     wbr: true
 27 |   };
 28 | 
 29 |   function parseToVirtual(html) {
 30 |     var skip = 0;
 31 |     var root = VirtualElement('root');
 32 |     var parent = root;
 33 |     var parser = new parse5.SAXParser({
 34 |       decodeHtmlEntities: false
 35 |     });
 36 |     parser.on('doctype', function(name, publicId, systemId /*, [location] */) {
 37 |       root.children().push('<!DOCTYPE ' + name + '>');
 38 |     });
 39 | 
 40 |     parser.on('startTag', function(tagName, attrsArray, selfClosing /*, [location] */) {
 41 |       var attrs = {};
 42 |       var length = attrsArray.length;
 43 |       var index = -1;
 44 |       while (++index < length) {
 45 |         attrs[attrsArray[index].name] = attrsArray[index].value;
 46 |       }
 47 | 
 48 |       selfClosing = selfClosing || selfClosingTags[tagName];
 49 | 
 50 |       var element = VirtualElement(tagName);
 51 |       if (parent !== root) {
 52 |         element._parent = parent;
 53 |       }
 54 |       element._attributes = attrs;
 55 |       element._isSelfClosing = selfClosing;
 56 |       element._haveAttributes = true;
 57 |       element._createAttributeExpressions();
 58 | 
 59 |       if (attrs.style) {
 60 |         element._style = generateStyleObject(attrs.style);
 61 |         element._haveStyle = true;
 62 |         attrs.style = null;
 63 |       }
 64 | 
 65 |       if (parent) {
 66 |         parent._children.push(element);
 67 |       }
 68 | 
 69 |       if (!selfClosing) {
 70 |         parent = element;
 71 |       }
 72 | 
 73 |       if (skip) {
 74 |         attrs['data-query'] = null;
 75 |       }
 76 | 
 77 |       if (!selfClosing && (skip || tagName == 'script' || tagName == 'style' || tagName == 'code' || element.hasClass('bl-skip'))) {
 78 |         skip += 1;
 79 |       }
 80 |     });
 81 | 
 82 |     parser.on('endTag', function(tagName) {
 83 |       var newParent = parent._parent;
 84 | 
 85 |       if (parent && parent.tagName() !== tagName.toLowerCase()) {
 86 |         //TODO Improve with adding information about the location inside the file.
 87 |         console.warn('tag missmatch found closing tag for ' + tagName + ' while expecting to close ' + parent.tagName() + '!');
 88 |       }
 89 | 
 90 |       if (skip) {
 91 |         skip -= 1;
 92 |         if (skip === 0) {
 93 |           parent._innerHTML = parent.renderChildren();
 94 |         }
 95 |       }
 96 |       if (parent) {
 97 |         parent = newParent || root;
 98 |       }
 99 |     });
100 | 
101 |     parser.on('text', function(text /*, [location] */) {
102 |       if (parent) {
103 |         if (skip === 0) {
104 |           parent._children.push(Expression.Create(text) || text);
105 |         } else {
106 |           parent._children.push(text);
107 |         }
108 |       }
109 |     });
110 | 
111 |     parser.on('comment', function(text /*, [location] */) {
112 |       var trimmedComment = text.replace(trimRegExp, '');
113 |       var comment;
114 | 
115 |       if (trimmedComment.indexOf('blocks') === 0) {
116 |         comment = new VirtualComment(text);
117 |         comment._parent = parent;
118 |         comment._attributes[dataQueryAttr] = trimmedComment.substring(6);
119 |         parent._children.push(comment);
120 |         parent = comment;
121 |       } else if (trimmedComment.indexOf('/blocks') === 0) {
122 |         parent = parent._parent;
123 |       } else {
124 |         parent._children.push('<!--' + text + '-->');
125 |       }
126 |     });
127 | 
128 |     parser.end(html);
129 | 
130 |     return root.children();
131 |   }
132 | 
133 |   // TODO: Refactor this because it is duplicate from query/createVirtual.js file
134 |   function generateStyleObject(styleString) {
135 |     var styles = styleString.split(';');
136 |     var styleObject = {};
137 |     var index;
138 |     var style;
139 |     var values;
140 | 
141 |     for (var i = 0; i < styles.length; i++) {
142 |       style = styles[i];
143 |       if (style) {
144 |         index = style.indexOf(':');
145 |         if (index != -1) {
146 |           values = [style.substring(0, index), style.substring(index + 1)];
147 |           styleObject[values[0].toLowerCase().replace(trimRegExp, '')] = values[1].replace(trimRegExp, '');
148 |         }
149 |       }
150 |     }
151 | 
152 |     return styleObject;
153 |   }
154 | 
155 |   return parseToVirtual;
156 | });


--------------------------------------------------------------------------------
/src/query.js:
--------------------------------------------------------------------------------
1 | define([
2 |   './query/ready',
3 |   './query/queries',
4 |   './query/observable',
5 |   './query/extenders',
6 |   './query/DomQuery',
7 |   './query/methods'
8 | ]);
9 | 


--------------------------------------------------------------------------------
/src/query/ChunkManager.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   './dom',
  4 |   './animation',
  5 |   './ElementsData'
  6 | ], function (blocks, dom, animation, ElementsData) {
  7 |   function ChunkManager(observable) {
  8 |     this.observable = observable;
  9 |     this.chunkLengths = {};
 10 |     this.dispose();
 11 |   }
 12 | 
 13 |   ChunkManager.prototype = {
 14 |     dispose: function () {
 15 |       this.childNodesCount = undefined;
 16 |       this.startIndex = 0;
 17 |       this.observableLength = undefined;
 18 |       this.startOffset = 0;
 19 |       this.endOffset = 0;
 20 |     },
 21 | 
 22 |     setStartIndex: function (index) {
 23 |       this.startIndex = index + this.startOffset;
 24 |     },
 25 | 
 26 |     setChildNodesCount: function (count) {
 27 |       if (this.childNodesCount === undefined) {
 28 |         this.observableLength = this.observable._getValue().length;
 29 |       }
 30 |       this.childNodesCount = count - (this.startOffset + this.endOffset);
 31 |     },
 32 | 
 33 |     chunkLength: function (wrapper) {
 34 |       var chunkLengths = this.chunkLengths;
 35 |       var id = ElementsData.id(wrapper);
 36 |       var length = chunkLengths[id] || (this.childNodesCount || wrapper.childNodes.length) / (this.observableLength || this.observable._getValue().length);
 37 |       var result;
 38 | 
 39 |       if (blocks.isNaN(length) || length === Infinity) {
 40 |         result = 0;
 41 |       } else {
 42 |         result = Math.round(length);
 43 |       }
 44 | 
 45 |       chunkLengths[id] = result;
 46 | 
 47 |       return result;
 48 |     },
 49 | 
 50 |     getAt: function (wrapper, index) {
 51 |       var chunkLength = this.chunkLength(wrapper);
 52 |       var childNodes = wrapper.childNodes;
 53 |       var result = [];
 54 | 
 55 |       for (var i = 0; i < chunkLength; i++) {
 56 |         result[i] = childNodes[index * chunkLength + i + this.startIndex];
 57 |       }
 58 |       return result;
 59 |     },
 60 | 
 61 |     insertAt: function (wrapper, index, chunk) {
 62 |       animation.insert(
 63 |         wrapper,
 64 |         this.chunkLength(wrapper) * index + this.startIndex,
 65 |         blocks.isArray(chunk) ? chunk : [chunk]);
 66 |     },
 67 | 
 68 |     remove: function (index, howMany) {
 69 |       var _this = this;
 70 | 
 71 |       this.each(function (domElement) {
 72 |         for (var j = 0; j < howMany; j++) {
 73 |           _this._removeAt(domElement, index);
 74 |         }
 75 |       });
 76 | 
 77 |       ElementsData.collectGarbage();
 78 | 
 79 |       this.dispose();
 80 | 
 81 |       this.observable._indexes.splice(index, howMany);
 82 |     },
 83 | 
 84 |     add: function (addItems, index) {
 85 |       var _this = this;
 86 |       var observable = this.observable;
 87 | 
 88 |       blocks.each(addItems, function (item, i) {
 89 |         observable._indexes.splice(index + i, 0, blocks.observable(index + i));
 90 |       });
 91 | 
 92 |       this.each(function (domElement, virtualElement) {
 93 |         var domQuery = blocks.domQuery(domElement);
 94 |         var context = blocks.context(domElement);
 95 |         var html = '';
 96 |         var syncIndex;
 97 | 
 98 |         domQuery.contextBubble(context, function () {
 99 |           syncIndex = domQuery.getSyncIndex();
100 |           for (var i = 0; i < addItems.length; i++) {
101 |             domQuery.dataIndex(blocks.observable.getIndex(observable, i + index, true));
102 |             domQuery.pushContext(addItems[i]);
103 |             html += virtualElement.renderChildren(domQuery, syncIndex + (i + index));
104 |             domQuery.popContext();
105 |             domQuery.dataIndex(undefined);
106 |           }
107 |         });
108 | 
109 |         if (domElement.childNodes.length === 0) {
110 |           dom.html(domElement, html);
111 |           domQuery.createElementObservableDependencies(domElement.childNodes);
112 |         } else {
113 |           var fragment = domQuery.createFragment(html);
114 |           _this.insertAt(domElement, index, fragment);
115 |         }
116 |       });
117 | 
118 |       this.dispose();
119 |     },
120 |     smoothIndexes: function () {
121 |       for (var i = 0; i < this.observable._indexes.length; i++) {
122 |         this.observable._indexes[i](i);
123 |       }
124 |     },
125 |     each: function (callback) {
126 |       var i = 0;
127 |       var domElements = this.observable._elements;
128 | 
129 |       for (; i < domElements.length; i++) {
130 |         var data = domElements[i];
131 |         if (!data.element) {
132 |           data.element = ElementsData.data(data.elementId).dom;
133 |         }
134 |         this.setup(data.element, callback);
135 |       }
136 |     },
137 | 
138 |     setup: function (domElement, callback) {
139 |       if (!domElement) {
140 |         return;
141 |       }
142 | 
143 |       var eachData = ElementsData.data(domElement).eachData;
144 |       var element;
145 |       var commentId;
146 |       var commentIndex;
147 |       var commentElement;
148 | 
149 |       if (!eachData || eachData.id != this.observable.__id__) {
150 |         return;
151 |       }
152 | 
153 |       element = eachData.element;
154 |       this.startOffset = eachData.startOffset;
155 |       this.endOffset = eachData.endOffset;
156 | 
157 |       if (domElement.nodeType == 1) {
158 |         // HTMLElement
159 |         this.setStartIndex(0);
160 |         this.setChildNodesCount(domElement.childNodes.length);
161 |         callback(domElement, element, domElement);
162 |       } else {
163 |         // Comment
164 |         commentId = ElementsData.id(domElement);
165 |         commentElement = domElement.parentNode.firstChild;
166 |         commentIndex = 0;
167 |         while (commentElement != domElement) {
168 |           commentElement = commentElement.nextSibling;
169 |           commentIndex++;
170 |         }
171 |         this.setStartIndex(commentIndex + 1);
172 |         while (commentElement && (commentElement.nodeType != 8 || commentElement.nodeValue.indexOf(commentId + ':/blocks') != 1)) {
173 |           commentElement = commentElement.nextSibling;
174 |           commentIndex++;
175 |         }
176 |         this.setChildNodesCount(commentIndex - this.startIndex);
177 |         callback(domElement.parentNode, element, domElement);
178 |       }
179 |     },
180 | 
181 |     _removeAt: function (wrapper, index) {
182 |       var chunkLength = this.chunkLength(wrapper);
183 | 
184 |       animation.remove(
185 |         wrapper,
186 |         chunkLength * index + this.startIndex,
187 |         chunkLength);
188 |     }
189 |   };
190 |   return ChunkManager;
191 | });
192 | 


--------------------------------------------------------------------------------
/src/query/ElementsData.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   './var/dataIdAttr',
  3 |   './var/virtualElementIdentity',
  4 |   './VirtualElement'
  5 | ], function (dataIdAttr, virtualElementIdentity, VirtualElement) {
  6 |     var data = {};
  7 |     var globalId = 1;
  8 | 
  9 |     function getDataId(element) {
 10 |       var result = element ? VirtualElement.Is(element) ? element._state ? element._state.attributes[dataIdAttr] : element._attributes[dataIdAttr] :
 11 |         element.nodeType == 1 ? element.getAttribute(dataIdAttr) :
 12 |           element.nodeType == 8 ? /\s+(\d+):[^\/]/.exec(element.nodeValue) :
 13 |             null :
 14 |         null;
 15 | 
 16 |       return blocks.isArray(result) ? result[1] : result;
 17 |     }
 18 | 
 19 |     function setDataId(element, id) {
 20 |       if (VirtualElement.Is(element)) {
 21 |         element.attr(dataIdAttr, id);
 22 |       } else if (element.nodeType == 1) {
 23 |         element.setAttribute(dataIdAttr, id);
 24 |       }
 25 |     }
 26 | 
 27 |     var ElementsData = {
 28 |       id: function (element) {
 29 |         return getDataId(element);
 30 |       },
 31 | 
 32 |       /* @if SERVER */
 33 |       reset: function () {
 34 |         data = {};
 35 |         globalId = 1;
 36 |       },
 37 |       /* @endif */
 38 | 
 39 |       collectGarbage: function () {
 40 |         blocks.each(data, function (value) {
 41 |           if (value && value.dom && !document.body.contains(value.dom)) {
 42 |             ElementsData.clear(value.id, true);
 43 |           }
 44 |         });
 45 |       },
 46 | 
 47 |       createIfNotExists: function (element) {
 48 |         var isVirtual = element && element.__identity__ == virtualElementIdentity;
 49 |         var currentData;
 50 |         var id;
 51 | 
 52 |         if (isVirtual) {
 53 |           currentData = data[element._getAttr(dataIdAttr)];
 54 |         } else {
 55 |           currentData = data[element && getDataId(element)];
 56 |         }
 57 | 
 58 |         if (!currentData) {
 59 |           id = globalId++;
 60 |           if (element) {
 61 |             if (isVirtual && element._each) {
 62 |               element._haveAttributes = true;
 63 |               if (element._state) {
 64 |                 element._state.attributes[dataIdAttr] = id;
 65 |               } else {
 66 |                 element._attributes[dataIdAttr] = id;
 67 |               }
 68 |             } else {
 69 |               setDataId(element, id);
 70 |             }
 71 |           }
 72 | 
 73 |           // if element is not defined then treat it as expression
 74 |           if (!element) {
 75 |             currentData = data[id] = {
 76 |               id: id,
 77 |               observables: {}
 78 |             };
 79 |           } else {
 80 |             currentData = data[id] = {
 81 |               id: id,
 82 |               virtual: isVirtual ? element : null,
 83 |               animating: 0,
 84 |               observables: {},
 85 |               preprocess: isVirtual
 86 |             };
 87 |           }
 88 |         }
 89 | 
 90 |         return currentData;
 91 |       },
 92 | 
 93 |       byId: function (id) {
 94 |         return data[id];
 95 |       },
 96 | 
 97 |       data: function (element, name, value) {
 98 |         var result = data[getDataId(element) || element];
 99 |         if (!result) {
100 |           return;
101 |         }
102 |         if (arguments.length == 1) {
103 |           return result;
104 |         } else if (arguments.length > 2) {
105 |           result[name] = value;
106 |         }
107 |         return result[name];
108 |       },
109 | 
110 |       clear: function (element, force) {
111 |         var id = getDataId(element) || element;
112 |         var currentData = data[id];
113 | 
114 |         if (currentData && (!currentData.haveData || force)) {
115 |           blocks.each(currentData.observables, function (value) {
116 |             for (var i = 0; i < value._elements.length; i++) {
117 |               if (value._elements[i].elementId == currentData.id) {
118 |                 value._elements.splice(i, 1);
119 |                 i--;
120 |               }
121 |             }
122 | 
123 |             if (value._expressions) {
124 |               for (i = 0; i < value._expressions.length; i++) {
125 |                 if (value._expressions[i].elementId == currentData.id) {
126 |                   value._expressions.splice(i, 1);
127 |                   i--;
128 |                 }
129 |               }
130 |               value._expressionKeys[currentData.id] = null;
131 |             }
132 |           });
133 |           data[id] = undefined;
134 |           if (VirtualElement.Is(element)) {
135 |             element.removeAttr(dataIdAttr);
136 |           } else if (element.nodeType == 1) {
137 |             element.removeAttribute(dataIdAttr);
138 |           }
139 |         }
140 |       }
141 |     };
142 |     return ElementsData;
143 | });
144 | 


--------------------------------------------------------------------------------
/src/query/Expression.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   './var/parameterQueryCache',
  4 |   '../modules/Escape',
  5 |   './ElementsData',
  6 |   './Observer'
  7 | ], function (blocks, parameterQueryCache, Escape, ElementsData, Observer) {
  8 |   function cloneExpression () {
  9 |     var element = this.element;
 10 |     this.element = null;
 11 |     this.clone = null;
 12 |     var clone = blocks.clone(this, true);
 13 |     clone.clone = this.clone = cloneExpression;
 14 |     this.element = element;
 15 |     return clone;
 16 |   }
 17 |   var Expression = {
 18 |     Html: 0,
 19 |     ValueOnly: 2,
 20 |     Raw: 3,
 21 |     NodeWise: 4,
 22 | 
 23 |     Create: function (text, attributeName, element) {
 24 |       var index = -1;
 25 |       var endIndex = 0;
 26 |       var result = [];
 27 |       var character;
 28 |       var startIndex;
 29 |       var match;
 30 | 
 31 |       while (text.length > ++index) {
 32 |         character = text.charAt(index);
 33 | 
 34 |         if (character == '{' && text.charAt(index + 1) == '{') {
 35 |           startIndex = index + 2;
 36 |         } else if (character == '}' && text.charAt(index + 1) == '}') {
 37 |           if (startIndex) {
 38 |             match = text.substring(startIndex, index);
 39 |             if (!attributeName) {
 40 |               match = match
 41 |                 .replace(/&amp;/g, '&')
 42 |                 .replace(/&quot;/g, '"')
 43 |                 .replace(/&#39;/g, '\'')
 44 |                 .replace(/&lt;/g, '<')
 45 |                 .replace(/&gt;/g, '>');
 46 |             }
 47 | 
 48 |             character = text.substring(endIndex, startIndex - 2);
 49 |             if (character) {
 50 |               result.push({
 51 |                 value: character
 52 |               });
 53 |             }
 54 | 
 55 |             result.push({
 56 |               expression: match,
 57 |               attributeName: attributeName
 58 |             });
 59 | 
 60 |             endIndex = index + 2;
 61 |           }
 62 |           startIndex = 0;
 63 |         }
 64 |       }
 65 | 
 66 |       character = text.substring(endIndex);
 67 |       if (character) {
 68 |         result.push({
 69 |           value: character
 70 |         });
 71 |       }
 72 | 
 73 |       result.text = text;
 74 |       result.attributeName = attributeName;
 75 |       result.element = element;
 76 |       result.isExpression = true;
 77 |       result.nodeLength = 0;
 78 |       result.clone = cloneExpression;
 79 |       return match ? result : null;
 80 |     },
 81 | 
 82 |     GetValue: function (context, elementData, expression, type) {
 83 |       var nodeWise = type == Expression.NodeWise;
 84 |       var value = nodeWise || type == Expression.Raw ? [] : '';
 85 |       var length = expression.length;
 86 |       var index = -1;
 87 |       var chunk;
 88 |       var lastNodeIndex;
 89 |       var tempValue;
 90 | 
 91 |       type = type||Expression.Html;
 92 | 
 93 |       if (!context) {
 94 |         return expression.text;
 95 |       }
 96 | 
 97 |       if (type !== Expression.ValueOnly) {
 98 |         expression.nodeLength = 0; // reset for recalculation
 99 |       }
100 | 
101 |       if (length == 1) {
102 |         if (nodeWise) {
103 |           lastNodeIndex = expression.nodeLength;
104 |           tempValue = Expression.Execute(context, elementData, expression[0], expression, type);
105 | 
106 |           if ((expression.nodeLength - lastNodeIndex) == 2) {
107 |             value[expression.nodeLength - 2] = null;
108 |           }
109 |           value[expression.nodeLength - 1] = tempValue;
110 |         } else {
111 |           value = Expression.Execute(context, elementData, expression[0], expression, type);
112 |         }
113 |       } else {
114 |         while (++index < length) {
115 |           lastNodeIndex = expression.nodeLength;
116 |           chunk = expression[index];
117 | 
118 |           if (chunk.value) {
119 |             if (type !== Expression.ValueOnly && expression.nodeLength === 0) {
120 |               expression.nodeLength++;
121 |             }
122 |             tempValue = chunk.value;
123 |           } else {
124 |             tempValue = Expression.Execute(context, elementData, chunk, expression, type);
125 |             if (nodeWise && (expression.nodeLength - lastNodeIndex) == 2)  {
126 |               value[expression.nodeLength - 2] = null; // dom comments that got rendered in the expression
127 |             }
128 |           }
129 | 
130 |           if (nodeWise) {
131 |             value[expression.nodeLength - 1] = (value[expression.nodeLength - 1] || '') + tempValue;
132 |           } else if (type == Expression.Raw) {
133 |             value.push(tempValue);
134 |           } else {
135 |             value +=  tempValue;
136 |           }
137 |         }
138 |       }
139 | 
140 |       expression.lastResult = value;
141 | 
142 |       return value;
143 |     },
144 | 
145 |     Execute: function (context, elementData, expressionData, entireExpression, type) {
146 |       var expression = expressionData.expression;
147 |       var attributeName = expressionData.attributeName;
148 |       var isObservable;
149 |       var expressionObj;
150 |       var observables;
151 |       var result;
152 |       var value;
153 |       var func;
154 | 
155 |       // jshint -W054
156 |       // Disable JSHint error: The Function constructor is a form of eval
157 |       func = parameterQueryCache[expression] = parameterQueryCache[expression] ||
158 |         new Function('c', 'with(c){with($this){ return ' + expression + '}}'/*@if DEBUG */ + '//# sourceURL=' + encodeURI(entireExpression.text)/* @endif */);
159 | 
160 |       Observer.startObserving();
161 | 
162 |       /* @if DEBUG */ {
163 |         try {
164 |           value = func(context);
165 |         } catch (ex) {
166 |           blocks.debug.expressionFail(expression, entireExpression.element);
167 |         }
168 |       } /* @endif */
169 | 
170 |       value = func(context);
171 | 
172 |       isObservable = blocks.isObservable(value);
173 |       result = isObservable ? value() : value;
174 |       result = result == null ? '' : result.toString();
175 |       result = attributeName ? Escape.forHTMLAttributes(result) : Escape.forHTML(result);
176 | 
177 |       observables = Observer.stopObserving();
178 | 
179 |       if (type == Expression.Raw) {
180 |         return {observables: observables, result: result, value: value};
181 |       }
182 | 
183 |       if (type != Expression.ValueOnly && type != Expression.NodeWise && (isObservable || observables.length)) {
184 |         if (!attributeName) {
185 |           elementData = ElementsData.createIfNotExists();
186 |         }
187 |         if (elementData) {
188 |           elementData.haveData = true;
189 | 
190 |           expressionObj = {
191 |             length: result.length,
192 |             attr: attributeName,
193 |             context: context,
194 |             elementId: elementData.id,
195 |             expression: expression,
196 |             entire: entireExpression
197 |           };
198 | 
199 |           blocks.each(observables, function (observable) {
200 |             if (!observable._expressionKeys[elementData.id + (attributeName ||'expression') +'[' + expression + ']']) {
201 |               observable._expressionKeys[elementData.id + (attributeName ||'expression') +'[' + expression + ']'] = true;
202 |               observable._expressions.push(expressionObj);
203 |             }
204 | 
205 |             elementData.observables[observable.__id__ + (attributeName || 'expression') + '[' + expression + ']'] = observable;
206 |           });
207 |         }
208 |         if (!attributeName) {
209 |           entireExpression.nodeLength += 2;
210 |           result = '<!-- ' + elementData.id + ':blocks -->' + result;
211 |         }
212 |       } else if (!attributeName && type !== Expression.ValueOnly) {
213 |         if (type == Expression.NodeWise && isObservable) {
214 |           entireExpression.nodeLength += 2;
215 |         } else if (entireExpression.nodeLength === 0) {
216 |           entireExpression.nodeLength++;
217 |         }
218 |       }
219 | 
220 |       return result;
221 |     }
222 |   };
223 | 
224 |   return Expression;
225 | });
226 | 


--------------------------------------------------------------------------------
/src/query/Observer.js:
--------------------------------------------------------------------------------
 1 | define(function () {
 2 |   var Observer = (function () {
 3 |     var stack = [];
 4 | 
 5 |     return {
 6 |       startObserving: function () {
 7 |         stack.push([]);
 8 |       },
 9 | 
10 |       stopObserving: function () {
11 |         return stack.pop();
12 |       },
13 | 
14 |       currentObservables: function () {
15 |         return stack[stack.length - 1];
16 |       },
17 | 
18 |       registerObservable: function (newObservable) {
19 |         var observables = stack[stack.length - 1];
20 |         var alreadyExists = false;
21 | 
22 |         if (observables) {
23 |           blocks.each(observables, function (observable) {
24 |             if (observable === newObservable) {
25 |               alreadyExists = true;
26 |               return false;
27 |             }
28 |           });
29 |           if (!alreadyExists) {
30 |             observables.push(newObservable);
31 |           }
32 |         }
33 |       }
34 |     };
35 |   })();
36 | 
37 |   return Observer;
38 | });
39 | 


--------------------------------------------------------------------------------
/src/query/VirtualComment.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   '../var/trimRegExp',
 4 |   './var/dataIdAttr',
 5 |   './VirtualElement',
 6 |   '../modules/Escape'
 7 | ], function (blocks, trimRegExp, dataIdAttr, VirtualElement, Escape) {
 8 |   function VirtualComment(commentText) {
 9 |     if (!VirtualComment.prototype.isPrototypeOf(this)) {
10 |       return new VirtualComment(commentText);
11 |     }
12 | 
13 |     this.__Class__();
14 | 
15 |     if (commentText.nodeType == 8) {
16 |       this._commentText = commentText.nodeValue;
17 |       this._el = commentText;
18 |     } else {
19 |       this._commentText = commentText;
20 |     }
21 |   }
22 | 
23 |   blocks.VirtualComment = blocks.inherit(VirtualElement, VirtualComment, {
24 |     renderBeginTag: function () {
25 |       var dataId = this._getAttr(dataIdAttr);
26 |       var html = '<!-- ';
27 | 
28 |       if (dataId) {
29 |         html += dataId + ':';
30 |       }
31 |       html += Escape.forHTML(this._commentText.replace(trimRegExp, '')) + ' -->';
32 | 
33 |       return html;
34 |     },
35 | 
36 |     renderEndTag: function () {
37 |       var dataId = this._getAttr(dataIdAttr);
38 |       var html = '<!-- ';
39 | 
40 |       if (dataId) {
41 |         html += dataId + ':';
42 |       }
43 |       html += '/blocks -->';
44 |       return html;
45 |     },
46 | 
47 |     _executeAttributeExpressions: blocks.noop
48 |   });
49 | 
50 |   VirtualComment.Is = function (value) {
51 |     return VirtualComment.prototype.isPrototypeOf(value);
52 |   };
53 | 
54 |   return VirtualComment;
55 | });
56 | 


--------------------------------------------------------------------------------
/src/query/addListener.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../modules/Event'
 3 | ], function (Event) {
 4 |   function addListener(element, eventName, callback) {
 5 |     if (element.addEventListener && eventName != 'propertychange') {
 6 |       element.addEventListener(eventName, function (event) {
 7 |         callback.call(this, Event.fix(event));
 8 |       }, false);
 9 |     } else if (element.attachEvent) {
10 |       element.attachEvent('on' + eventName, function (event) {
11 |         callback.call(this, Event.fix(event));
12 |       });
13 |     }
14 |   }
15 | 
16 |   return addListener;
17 | });
18 | 


--------------------------------------------------------------------------------
/src/query/browser.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core'
 3 | ], function (blocks) {
 4 |   var browser = {};
 5 | 
 6 |   function parseVersion(matches) {
 7 |     if (matches) {
 8 |       return parseFloat(matches[1]);
 9 |     }
10 |     return undefined;
11 |   }
12 | 
13 |   if (typeof document !== 'undefined') {
14 |     blocks.extend(browser, {
15 |       IE: document && (function () {
16 |         var version = 3;
17 |         var div = document.createElement('div');
18 |         var iElems = div.getElementsByTagName('i');
19 | 
20 |         /* jshint noempty: false */
21 |         // Disable JSHint error: Empty block
22 |         // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
23 |         while (
24 |           div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
25 |           iElems[0]
26 |           ) { }
27 |         return version > 4 ? version : undefined;
28 |       }()),
29 | 
30 |       Opera: (window && window.navigator && window.opera && window.opera.version && parseInt(window.opera.version(), 10)) || undefined,
31 | 
32 |       Safari: window && window.navigator && parseVersion(window.navigator.userAgent.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),
33 | 
34 |       Firefox: window && window.navigator && parseVersion(window.navigator.userAgent.match(/Firefox\/([^ ]*)/))
35 |     });
36 |   }
37 | 
38 |   return browser;
39 | });
40 | 


--------------------------------------------------------------------------------
/src/query/createFragment.js:
--------------------------------------------------------------------------------
 1 | define(function () {
 2 |   function createFragment(html) {
 3 |     var fragment = document.createDocumentFragment();
 4 |     var temp = document.createElement('div');
 5 |     var count = 1;
 6 |     var table = '<table>';
 7 |     var tableEnd = '</table>';
 8 |     var tbody = '<tbody>';
 9 |     var tbodyEnd = '</tbody>';
10 |     var tr = '<tr>';
11 |     var trEnd = '</tr>';
12 | 
13 |     html = html.toString();
14 | 
15 |     if ((html.indexOf('<option') != -1) && html.indexOf('<select') == -1) {
16 |       html = '<select>' + html + '</select>';
17 |       count = 2;
18 |     } else if (html.indexOf('<table') == -1) {
19 |       if (html.match(/<(tbody|thead|tfoot)/)) {
20 |         count = 2;
21 |         html = table + html + tableEnd;
22 |       } else if (html.indexOf('<tr') != -1) {
23 |         count = 3;
24 |         html = table + tbody + html + tbodyEnd + tableEnd;
25 |       } else if (html.match(/<(td|th)/)) {
26 |         count = 4;
27 |         html = table + tbody + tr + html + trEnd + tbodyEnd + tableEnd;
28 |       }
29 |     }
30 | 
31 | 
32 |     temp.innerHTML = 'A<div>' + html + '</div>';
33 | 
34 |     while (count--) {
35 |       temp = temp.lastChild;
36 |     }
37 | 
38 |     while (temp.firstChild) {
39 |       fragment.appendChild(temp.firstChild);
40 |     }
41 | 
42 |     return fragment;
43 |   }
44 | 
45 |   return createFragment;
46 | });


--------------------------------------------------------------------------------
/src/query/createVirtual.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../var/trimRegExp',
  3 |   './var/dataQueryAttr',
  4 |   './browser',
  5 |   './Expression',
  6 |   './VirtualElement',
  7 |   './VirtualComment',
  8 |   './serverData'
  9 | ], function (trimRegExp, dataQueryAttr, browser, Expression, VirtualElement, VirtualComment, serverData) {
 10 |   function createVirtual(htmlElement, parentElement) {
 11 |     var elements = [];
 12 |     var element;
 13 |     var tagName;
 14 |     var elementAttributes;
 15 |     var htmlAttributes;
 16 |     var htmlAttribute;
 17 |     var nodeType;
 18 |     var commentText;
 19 |     var commentTextTrimmed;
 20 |     var data;
 21 | 
 22 |     while (htmlElement) {
 23 |       nodeType = htmlElement.nodeType;
 24 |       if (nodeType == 1) {
 25 |         // HtmlDomElement
 26 |         tagName = htmlElement.tagName.toLowerCase();
 27 |         element = new VirtualElement(htmlElement);
 28 |         element._tagName = tagName;
 29 |         element._parent = parentElement;
 30 |         if (parentElement) {
 31 |           element._each = parentElement._each || parentElement._childrenEach;
 32 |         }
 33 |         element._haveAttributes = false;
 34 |         htmlAttributes = htmlElement.attributes;
 35 |         elementAttributes = {};
 36 |         for (var i = 0; i < htmlAttributes.length; i++) {
 37 |           htmlAttribute = htmlAttributes[i];
 38 |           // the style should not be part of the attributes. The style is handled individually.
 39 |           if (htmlAttribute.nodeName !== 'style' &&
 40 |             (htmlAttribute.specified ||
 41 |               //IE7 wil return false for .specified for the "value" attribute - WTF!
 42 |             (browser.IE < 8 && htmlAttribute.nodeName == 'value' && htmlAttribute.nodeValue))) {
 43 |             elementAttributes[htmlAttribute.nodeName.toLowerCase()] = browser.IE < 11 ? htmlAttribute.nodeValue : htmlAttribute.value;
 44 |             element._haveAttributes = true;
 45 |           }
 46 |         }
 47 |         element._attributes = elementAttributes;
 48 |         element._createAttributeExpressions(serverData);
 49 | 
 50 |         if (htmlElement.style.cssText) {
 51 |           element._haveStyle = true;
 52 |           element._style = generateStyleObject(htmlElement.style.cssText);
 53 |         }
 54 | 
 55 |         setIsSelfClosing(element);
 56 |         if (tagName == 'script' || tagName == 'style' || tagName == 'code' || element.hasClass('bl-skip')) {
 57 |           element._innerHTML = htmlElement.innerHTML;
 58 |         } else {
 59 |           element._children = createVirtual(htmlElement.childNodes[0], element);
 60 |         }
 61 | 
 62 |         elements.push(element);
 63 |       } else if (nodeType == 3) {
 64 |         // TextNode
 65 |         //if (htmlElement.data.replace(trimRegExp, '').replace(/(\r\n|\n|\r)/gm, '') !== '') {
 66 |         //
 67 |         //}
 68 |         data = htmlElement.data;
 69 |         elements.push(Expression.Create(data, null, htmlElement) || data);
 70 |       } else if (nodeType == 8) {
 71 |         // Comment
 72 |         commentText = htmlElement.nodeValue;
 73 |         commentTextTrimmed = commentText.replace(trimRegExp, '');
 74 |         if (commentTextTrimmed.indexOf('blocks') === 0) {
 75 |           element = new VirtualComment(htmlElement);
 76 |           element._parent = parentElement;
 77 |           element._attributes[dataQueryAttr] = commentTextTrimmed.substring(6);
 78 |           data = createVirtual(htmlElement.nextSibling, element);
 79 |           element._children = data.elements;
 80 |           element._el._endElement = data.htmlElement;
 81 |           htmlElement = data.htmlElement || htmlElement;
 82 |           elements.push(element);
 83 |         } else if (VirtualComment.Is(parentElement) && commentTextTrimmed.indexOf('/blocks') === 0) {
 84 |           return {
 85 |             elements: elements,
 86 |             htmlElement: htmlElement
 87 |           };
 88 |         } else if (VirtualComment.Is(parentElement)) {
 89 |           elements.push('<!--' + commentText + '-->');
 90 |         } else if (serverData.hasData) {
 91 |           var number = parseInt(/[0-9]+/.exec(commentTextTrimmed), 10);
 92 |           if (!blocks.isNaN(number) && serverData.data[number]) {
 93 |             elements.push(Expression.Create(serverData.data[number]));
 94 |           }
 95 |         } else if (commentTextTrimmed.indexOf('/blocks') !== 0) {
 96 |           elements.push('<!--' + commentText + '-->');
 97 |         }
 98 |       }
 99 |       htmlElement = htmlElement.nextSibling;
100 |     }
101 |     return elements;
102 |   }
103 | 
104 |   function generateStyleObject(styleString) {
105 |     var styles = styleString.split(';');
106 |     var styleObject = {};
107 |     var index;
108 |     var style;
109 |     var values;
110 | 
111 |     for (var i = 0; i < styles.length; i++) {
112 |       style = styles[i];
113 |       if (style) {
114 |         index = style.indexOf(':');
115 |         if (index != -1) {
116 |           values = [style.substring(0, index), style.substring(index + 1)];
117 |           styleObject[values[0].toLowerCase().replace(trimRegExp, '')] = values[1].replace(trimRegExp, '');
118 |         }
119 |       }
120 |     }
121 | 
122 |     return styleObject;
123 |   }
124 | 
125 |   var isSelfClosingCache = {};
126 |   function setIsSelfClosing(element) {
127 |     var tagName = element._tagName;
128 |     var domElement;
129 | 
130 |     if (isSelfClosingCache[tagName] !== undefined) {
131 |       element._isSelfClosing = isSelfClosingCache[tagName];
132 |       return;
133 |     }
134 |     domElement = document.createElement('div');
135 |     domElement.appendChild(document.createElement(tagName));
136 |     isSelfClosingCache[tagName] = element._isSelfClosing = domElement.innerHTML.indexOf('</') === -1;
137 |   }
138 | 
139 |   return createVirtual;
140 | });


--------------------------------------------------------------------------------
/src/query/dom.js:
--------------------------------------------------------------------------------
  1 | define([
  2 |   '../core',
  3 |   '../var/trimRegExp',
  4 |   '../modules/keys',
  5 |   './var/dataIdAttr',
  6 |   './on',
  7 |   './browser',
  8 |   './setClass',
  9 |   './animation',
 10 |   './createFragment'
 11 | ], function (blocks, trimRegExp, keys, dataIdAttr, on, browser, setClass, animation, createFragment) {
 12 | 
 13 |   var dom = blocks.dom = {
 14 |     valueTagNames: {
 15 |       input: true,
 16 |       textarea: true,
 17 |       select: true
 18 |     },
 19 | 
 20 |     valueTypes: {
 21 |       file: true,
 22 |       hidden: true,
 23 |       password: true,
 24 |       text: true,
 25 | 
 26 |       // New HTML5 Types
 27 |       color: true,
 28 |       date: true,
 29 |       datetime: true,
 30 |       'datetime-local': true,
 31 |       email: true,
 32 |       month: true,
 33 |       number: true,
 34 |       range: true,
 35 |       search: true,
 36 |       tel: true,
 37 |       time: true,
 38 |       url: true,
 39 |       week: true
 40 |     },
 41 | 
 42 |     props: {
 43 |       'for': true,
 44 |       'class': true,
 45 |       value: true,
 46 |       checked: true,
 47 |       tabindex: true,
 48 |       className: true,
 49 |       htmlFor: true
 50 |     },
 51 | 
 52 |     propFix: {
 53 |       'for': 'htmlFor',
 54 |       'class': 'className',
 55 |       tabindex: 'tabIndex'
 56 |     },
 57 | 
 58 |     attrFix: {
 59 |       className: 'class',
 60 |       htmlFor: 'for'
 61 |     },
 62 | 
 63 |     addClass: function (element, className) {
 64 |       if (element) {
 65 |         setClass('add', element, className);
 66 |       }
 67 |     },
 68 | 
 69 |     removeClass: function (element, className) {
 70 |       if (element) {
 71 |         setClass('remove', element, className);
 72 |       }
 73 |     },
 74 | 
 75 |     html: function (element, html) {
 76 |       if (element) {
 77 |         html = html.toString();
 78 |         if (element.nodeType == 8) {
 79 |           dom.comment.html(element, html);
 80 |         } else if (browser.IE < 10) {
 81 |           while (element.firstChild) {
 82 |             element.removeChild(element.firstChild);
 83 |           }
 84 |           element.appendChild(createFragment(html));
 85 |         } else {
 86 |           element.innerHTML = html;
 87 |         }
 88 |       }
 89 |     },
 90 | 
 91 |     css: function (element, name, value) {
 92 |       // IE7 will thrown an error if you try to set element.style[''] (with empty string)
 93 |       if (!element || !name) {
 94 |         return;
 95 |       }
 96 | 
 97 |       if (name == 'display') {
 98 |         animation.setVisibility(element, value == 'none' ? false : true);
 99 |       } else {
100 |         element.style[name] = value;
101 |       }
102 |     },
103 | 
104 |     on: function (element, eventName, handler) {
105 |       if (element) {
106 |         on(element, eventName, handler);
107 |       }
108 |     },
109 | 
110 |     off: function () {
111 | 
112 |     },
113 | 
114 |     removeAttr: function (element, attributeName) {
115 |       if (element && attributeName) {
116 |         dom.attr(element, attributeName, null);
117 |       }
118 |     },
119 | 
120 |     attr: function (element, attributeName, attributeValue) {
121 |       var isProperty = dom.props[attributeName];
122 |       attributeName = dom.propFix[attributeName.toLowerCase()] || attributeName;
123 | 
124 |       if ((blocks.core.skipExecution &&
125 |         blocks.core.skipExecution.element === element &&
126 |         blocks.core.skipExecution.attributeName == attributeName) ||
127 |         !element) {
128 |         return;
129 |       }
130 | 
131 |       if (element.nodeType == 8) {
132 |         dom.comment.attr(element, attributeName, attributeValue);
133 |         return;
134 |       }
135 | 
136 |       if (attributeName == 'checked') {
137 |         if (attributeValue != 'checked' &&
138 |           typeof attributeValue == 'string' &&
139 |           element.getAttribute('type') == 'radio' &&
140 |           attributeValue != element.value && element.defaultValue != null && element.defaultValue !== '') {
141 | 
142 |           attributeValue = false;
143 |         } else {
144 |           if (blocks.isArray(attributeValue)) {
145 |             attributeValue = attributeValue.indexOf(element.value) !== -1;
146 |           } else {
147 |             attributeValue = !!attributeValue;
148 |           }
149 |         }
150 |       }
151 | 
152 |       if (arguments.length === 1) {
153 |         return isProperty ? element[attributeName] : element.getAttribute(attributeName);
154 |       } else if (attributeValue != null) {
155 |         if (attributeName == 'value' && element.tagName.toLowerCase() == 'select') {
156 |           attributeValue = keys(blocks.toArray(attributeValue));
157 |           blocks.each(element.children, function (child) {
158 |             child.selected = !!attributeValue[child.value];
159 |           });
160 |         } else {
161 |           if (isProperty) {
162 |             element[attributeName] = attributeValue;
163 |           } else {
164 |             element.setAttribute(attributeName, attributeValue);
165 |           }
166 |         }
167 |       } else {
168 |         if (isProperty) {
169 |           if (attributeName == 'value' && element.tagName.toLowerCase() == 'select') {
170 |             element.selectedIndex = -1;
171 |           } else if (element[attributeName]) {
172 |             element[attributeName] = '';
173 |           }
174 |         } else {
175 |           element.removeAttribute(attributeName);
176 |         }
177 |       }
178 |     },
179 | 
180 |     comment: {
181 |       html: function (element, html) {
182 |         // var commentElement = this._element.nextSibling;
183 |         // var parentNode = commentElement.parentNode;
184 |         // parentNode.insertBefore(DomQuery.CreateFragment(html), commentElement);
185 |         // parentNode.removeChild(commentElement);
186 |         var commentElement = element;
187 |         var parentNode = commentElement.parentNode;
188 |         var currentElement = commentElement.nextSibling;
189 |         var temp;
190 |         var count = 0;
191 | 
192 |         while (currentElement && (currentElement.nodeType != 8 || currentElement.nodeValue.indexOf('/blocks') == -1)) {
193 |           count++;
194 |           temp = currentElement.nextSibling;
195 |           parentNode.removeChild(currentElement);
196 |           currentElement = temp;
197 |         }
198 | 
199 |         parentNode.insertBefore(createFragment(html), commentElement.nextSibling);
200 |         //parentNode.removeChild(currentElement);
201 |         return count;
202 |       },
203 | 
204 |       attr: function (element, attributeName, attributeValue) {
205 |         if (element && attributeName == dataIdAttr && attributeValue) {
206 |           var commentElement = element;
207 |           // TODO: This should be refactored
208 |           var endComment = element._endElement;
209 |           commentElement.nodeValue = ' ' + attributeValue + ':' + commentElement.nodeValue.replace(trimRegExp, '') + ' ';
210 |           endComment.nodeValue = ' ' + attributeValue + ':' + endComment.nodeValue.replace(trimRegExp, '') + ' ';
211 |           return this;
212 |         }
213 |         return this;
214 |       }
215 |     }
216 |   };
217 | 
218 |   return blocks.dom;
219 | });
220 | 


--------------------------------------------------------------------------------
/src/query/extenders.js:
--------------------------------------------------------------------------------
  1 | 
  2 | define([
  3 |   '../core',
  4 |   './ExtenderHelper'
  5 | ], function (blocks, ExtenderHelper) {
  6 | 
  7 |   /**
  8 |    * @memberof blocks.observable
  9 |    * @class extenders
 10 |    */
 11 | 
 12 |   /**
 13 |    * Extends the observable by adding a .view property which is filtered
 14 |    * based on the provided options
 15 |    *
 16 |    * @memberof extenders
 17 |    * @param {(Function|Object|String)} options - provide a callback function
 18 |    * which returns true or false, you could also provide an observable
 19 |    * @returns {blocks.observable} - Returns a new observable
 20 |    * containing a .view property with the filtered data
 21 |    */
 22 |   blocks.observable.filter = function (options) {
 23 |     var observable = ExtenderHelper.initExpressionExtender(this);
 24 |     var callback = options;
 25 | 
 26 |     if (!blocks.isFunction(callback) || blocks.isObservable(callback)) {
 27 |       callback = function (value) {
 28 |         var filter = blocks.unwrap(options);
 29 |         var filterString = String(filter).toLowerCase();
 30 |         value = String(blocks.unwrap(value)).toLowerCase();
 31 | 
 32 |         return !filter || value.indexOf(filterString) != -1;
 33 |       };
 34 |     }
 35 | 
 36 |     observable._operations.push({
 37 |       type: ExtenderHelper.operations.FILTER,
 38 |       filter: callback
 39 |     });
 40 | 
 41 |     return observable;
 42 |   };
 43 | 
 44 |   blocks.observable.step = function (options) {
 45 |     var observable = ExtenderHelper.initExpressionExtender(this);
 46 | 
 47 |     observable._operations.push({
 48 |       type: ExtenderHelper.operations.STEP,
 49 |       step: options
 50 |     });
 51 | 
 52 |     return observable;
 53 |   };
 54 | 
 55 |   /**
 56 |    * Extends the observable by adding a .view property in which the first n
 57 |    * items are skipped
 58 |    *
 59 |    * @memberof extenders
 60 |    * @param {(number|blocks.observable|Function)} value - The number of items to be skipped
 61 |    * @returns {blocks.observable} - Returns a new observable
 62 |    * containing a .view property with the manipulated data
 63 |    */
 64 |   blocks.observable.skip = function (value) {
 65 |     var observable = ExtenderHelper.initExpressionExtender(this);
 66 | 
 67 |     observable._operations.push({
 68 |       type: ExtenderHelper.operations.SKIP,
 69 |       skip: value
 70 |     });
 71 | 
 72 |     return observable;
 73 |   };
 74 | 
 75 |   /**
 76 |    * Extends the observable by adding a .view property in which there is
 77 |    * always maximum n items
 78 |    *
 79 |    * @memberof extenders
 80 |    * @param {(number|blocks.observable|Function)} value - The max number of items to be in the collection
 81 |    * @returns {blocks.observable} - Returns a new observable
 82 |    * containing a .view property with the manipulated data
 83 |    */
 84 |   blocks.observable.take = function (value) {
 85 |     var observable = ExtenderHelper.initExpressionExtender(this);
 86 | 
 87 |     observable._operations.push({
 88 |       type: ExtenderHelper.operations.TAKE,
 89 |       take: value
 90 |     });
 91 | 
 92 |     return observable;
 93 |   };
 94 | 
 95 |   /**
 96 |    * Extends the observable by adding a .view property which is sorted
 97 |    * based on the provided options
 98 |    *
 99 |    * @memberof extenders
100 |    * @param {(Function|string)} options - provide a callback sort function or field name to be sorted
101 |    * @returns {blocks.observable} - Returns a new observable
102 |    * containing a .view property with the sorted data
103 |    */
104 |   blocks.observable.sort = function (options) {
105 |     var observable = ExtenderHelper.initExpressionExtender(this);
106 | 
107 |     observable._operations.push({
108 |       type: ExtenderHelper.operations.SORT,
109 |       sort: options
110 |     });
111 | 
112 |     return observable;
113 |   };
114 | });
115 | 


--------------------------------------------------------------------------------
/src/query/getClassIndex.js:
--------------------------------------------------------------------------------
 1 | define(function () {
 2 |   function getClassIndex(classAttribute, className) {
 3 |     if (!classAttribute || typeof classAttribute !== 'string' || className == null) {
 4 |       return -1;
 5 |     }
 6 | 
 7 |     classAttribute = ' ' + classAttribute + ' ';
 8 |     return classAttribute.indexOf(' ' + className + ' ');
 9 |   }
10 | 
11 |   return getClassIndex;
12 | });


--------------------------------------------------------------------------------
/src/query/on.js:
--------------------------------------------------------------------------------
 1 | define([
 2 | 	'./addListener',
 3 |   './browser',
 4 | 	'./ElementsData'
 5 | ], function (addListener, browser, ElementsData) {
 6 | 
 7 | 	// addEventListener implementation that fixes old browser issues
 8 |   function on(element, eventName, handler) {
 9 |     if (Workarounds[eventName]) {
10 |       Workarounds[eventName](element, handler, function (eventName, callback) {
11 |         addListener(element, eventName, callback);
12 |       });
13 |     } else {
14 |       addListener(element, eventName, handler);
15 |     }
16 |   }
17 | 
18 |   var Workarounds = {
19 | 		// support for "oninput" in legacy browsers
20 |     input: function (element, handler, subscribe) {
21 |       var timeout;
22 | 
23 |       function call(e) {
24 |         clearTimeout(timeout);
25 |         handler(e);
26 |       }
27 | 
28 |       function deferCall() {
29 |         if (!timeout) {
30 |           timeout = setTimeout(call, 4);
31 |         }
32 |       }
33 | 
34 |       if (browser.IE < 10) {
35 |         subscribe('propertychange', function (e) {
36 |           if (e.originalEvent.propertyName === 'value') {
37 |             call(e);
38 |           }
39 |         });
40 | 
41 |         if (browser.IE == 8) {
42 |           subscribe('keyup', call);
43 |           subscribe('keydown', call);
44 |         }
45 |         if (browser.IE >= 8) {
46 |           globalSelectionChangeHandler(element, call);
47 |           subscribe('dragend', deferCall);
48 |         }
49 |       } else {
50 |         subscribe('input', call);
51 | 
52 |         if (browser.Safari < 7 && element.tagName.toLowerCase() == 'textarea') {
53 |           subscribe('keydown', deferCall);
54 |           subscribe('paste', deferCall);
55 |           subscribe('cut', deferCall);
56 |         } else if (browser.Opera < 11) {
57 |           subscribe('keydown', deferCall);
58 |         } else if (browser.Firefox < 4.0) {
59 |           subscribe('DOMAutoComplete', call);
60 |           subscribe('dragdrop', call);
61 |           subscribe('drop', call);
62 |         }
63 |       }
64 |     }
65 |   };
66 | 
67 |   var globalSelectionChangeHandler = (function () {
68 |     var isRegistered = false;
69 | 
70 |     function selectionChangeHandler(e) {
71 |       var element = this.activeElement;
72 |       var handler = element && ElementsData.data(element, 'selectionchange');
73 |       if (handler) {
74 |         handler(e);
75 |       }
76 |     }
77 | 
78 |     return function (element, handler) {
79 |       if (!isRegistered) {
80 |         addListener(element.ownerDocument, 'selectionchange', selectionChangeHandler);
81 |         isRegistered = true;
82 |       }
83 |       ElementsData.createIfNotExists(element).selectionChange = handler;
84 |     };
85 |   })();
86 | 
87 |   return on;
88 | });
89 | 


--------------------------------------------------------------------------------
/src/query/parseQuery.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../var/trimRegExp'
 3 | ], function (trimRegExp) {
 4 | 
 5 |   function parseQuery(query, callback, context) {
 6 |     var character = 0;
 7 |     var bracketsCount = 0;
 8 |     var curlyBracketsCount = 0;
 9 |     var squareBracketsCount = 0;
10 |     var isInSingleQuotes = false;
11 |     var isInDoubleQuotes = false;
12 |     var startIndex = 0;
13 |     var parameters = [];
14 |     var currentParameter;
15 |     var methodName;
16 | 
17 |     query = query || '';
18 | 
19 |     for (var i = 0; i < query.length; i++) {
20 |       character = query.charAt(i);
21 | 
22 |       if (!isInSingleQuotes && !isInDoubleQuotes) {
23 |         if (character == '[') {
24 |           squareBracketsCount++;
25 |         } else if (character == ']') {
26 |           squareBracketsCount--;
27 |         } else if (character == '{') {
28 |           curlyBracketsCount++;
29 |         } else if (character == '}') {
30 |           curlyBracketsCount--;
31 |         }
32 |       }
33 | 
34 |       if (curlyBracketsCount !== 0 || squareBracketsCount !== 0) {
35 |         continue;
36 |       }
37 | 
38 |       if (character == '\'') {
39 |         isInSingleQuotes = !isInSingleQuotes;
40 |       } else if (character == '"') {
41 |         isInDoubleQuotes = !isInDoubleQuotes;
42 |       }
43 | 
44 |       if (isInSingleQuotes || isInDoubleQuotes) {
45 |         continue;
46 |       }
47 | 
48 |       if (character == '(') {
49 |         if (bracketsCount === 0) {
50 |           methodName = query.substring(startIndex, i).replace(trimRegExp, '');
51 |           startIndex = i + 1;
52 |         }
53 |         bracketsCount++;
54 |       } else if (character == ')') {
55 |         bracketsCount--;
56 |         if (bracketsCount === 0) {
57 |           currentParameter = query.substring(startIndex, i).replace(trimRegExp, '');
58 |           if (currentParameter.length) {
59 |             parameters.push(currentParameter);
60 |           }
61 | 
62 |           if (methodName) {
63 |             methodName = methodName.replace(/^("|')+|("|')+$/g, ''); // trim single and double quotes
64 |             if (context) {
65 |               callback.call(context, methodName, parameters);
66 |             } else {
67 |               callback(methodName, parameters);
68 |             }
69 |           }
70 |           parameters = [];
71 |           methodName = undefined;
72 |         }
73 |       } else if (character == ',' && bracketsCount == 1) {
74 |         currentParameter = query.substring(startIndex, i).replace(trimRegExp, '');
75 |         if (currentParameter.length) {
76 |           parameters.push(currentParameter);
77 |         }
78 |         startIndex = i + 1;
79 |       } else if (character == '.' && bracketsCount === 0) {
80 |         startIndex = i + 1;
81 |       }
82 |     }
83 |   }
84 | 
85 |   return parseQuery;
86 | });


--------------------------------------------------------------------------------
/src/query/ready.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../core',
 3 |   '../modules/parseCallback',
 4 |   '../modules/Events'
 5 | ], function (blocks, parseCallback, Events) {
 6 |   // Implementation of blocks.domReady event
 7 |   (function () {
 8 |     blocks.isDomReady = false;
 9 | 
10 |     //blocks.elementReady = function (element, callback, thisArg) {
11 |     //  callback = parseCallback(callback, thisArg);
12 |     //  if (element) {
13 |     //    callback();
14 |     //  } else {
15 |     //    blocks.domReady(callback);
16 |     //  }
17 |     //};
18 | 
19 |     blocks.domReady = function (callback, thisArg) {
20 |       if (typeof document == 'undefined' || typeof window == 'undefined' ||
21 |         (window.__mock__ && document.__mock__)) {
22 |         return;
23 |       }
24 | 
25 |       callback = parseCallback(callback, thisArg);
26 |       if (blocks.isDomReady || document.readyState == 'complete' ||
27 |         (window.jQuery && window.jQuery.isReady)) {
28 |         blocks.isDomReady = true;
29 |         callback();
30 |       } else {
31 |         Events.on(blocks.core, 'domReady', callback);
32 |         handleReady();
33 |       }
34 |     };
35 | 
36 |     function handleReady() {
37 |       if (document.readyState === 'complete') {
38 |         setTimeout(ready);
39 |       } else if (document.addEventListener) {
40 |         document.addEventListener('DOMContentLoaded', completed, false);
41 |         window.addEventListener('load', completed, false);
42 |       } else {
43 |         document.attachEvent('onreadystatechange', completed);
44 |         window.attachEvent('onload', completed);
45 | 
46 |         var top = false;
47 |         try {
48 |           top = window.frameElement == null && document.documentElement;
49 |         } catch (e) { }
50 | 
51 |         if (top && top.doScroll) {
52 |           (function doScrollCheck() {
53 |             if (!blocks.isDomReady) {
54 |               try {
55 |                 top.doScroll('left');
56 |               } catch (e) {
57 |                 return setTimeout(doScrollCheck, 50);
58 |               }
59 | 
60 |               ready();
61 |             }
62 |           })();
63 |         }
64 |       }
65 |     }
66 | 
67 |     function completed() {
68 |       if (document.addEventListener || event.type == 'load' || document.readyState == 'complete') {
69 |         ready();
70 |       }
71 |     }
72 | 
73 |     function ready() {
74 |       if (!blocks.isDomReady) {
75 |         blocks.isDomReady = true;
76 |         Events.trigger(blocks.core, 'domReady');
77 |         Events.off(blocks.core, 'domReady');
78 |       }
79 |     }
80 |   })();
81 | });
82 | 


--------------------------------------------------------------------------------
/src/query/serverData.js:
--------------------------------------------------------------------------------
 1 | define([
 2 | ], function () {
 3 | 	var serverData = {hasData: false};
 4 | 	blocks.domReady(function () {
 5 | 		if (global.document && global.document.body) {
 6 | 			var data = document.body.getAttribute('data-blocks-server-data');
 7 | 			if (data) {
 8 | 				document.body.removeAttribute('data-blocks-server-data');
 9 | 				/* global JSON */
10 | 				serverData.data = JSON.parse(data.replace('&quot;', '"'));
11 | 				serverData.hasData = true;
12 | 			}
13 | 		}
14 | 	});
15 | 	return serverData;
16 | });


--------------------------------------------------------------------------------
/src/query/setClass.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../var/trimRegExp',
 3 |   './var/classAttr',
 4 |   './getClassIndex',
 5 |   './VirtualElement'
 6 | ], function (trimRegExp, classAttr, getClassIndex, VirtualElement) {
 7 | 
 8 |   var classListMultiArguments = true;
 9 |   if (typeof document !== 'undefined') {
10 |     var element = document.createElement('div');
11 |     if (element.classList) {
12 |       element.classList.add('a', 'b');
13 |       classListMultiArguments = element.className == 'a b';
14 |     }
15 |   }
16 | 
17 |   function setClass(type, element, classNames) {
18 |     if (classNames != null) {
19 |       classNames = blocks.isArray(classNames) ? classNames : classNames.toString().split(' ');
20 |       var i = 0;
21 |       var classAttribute;
22 |       var className;
23 |       var index;
24 | 
25 |       if (VirtualElement.Is(element)) {
26 |         classAttribute = element._getAttr(classAttr);
27 |       } else if (element.classList) {
28 |         if (classListMultiArguments) {
29 |           element.classList[type].apply(element.classList, classNames);
30 |         } else {
31 |           blocks.each(classNames, function (value) {
32 |             element.classList[type](value);
33 |           });
34 |         }
35 |         return;
36 |       } else {
37 |         classAttribute = element.className;
38 |       }
39 |       classAttribute = classAttribute || '';
40 | 
41 |       for (; i < classNames.length; i++) {
42 |         className = classNames[i];
43 |         index = getClassIndex(classAttribute, className);
44 |         if (type == 'add') {
45 |           if (index < 0) {
46 |             if (classAttribute !== '') {
47 |               className = ' ' + className;
48 |             }
49 |             classAttribute += className;
50 |           }
51 |         } else if (index != -1) {
52 |           classAttribute = (classAttribute.substring(0, index) + ' ' +
53 |           classAttribute.substring(index + className.length + 1, classAttribute.length)).replace(trimRegExp, '');
54 |         }
55 |       }
56 | 
57 |       if (VirtualElement.Is(element)) {
58 |         if (element._state) {
59 |           element._state.attributes[classAttr] = classAttribute;
60 |         } else {
61 |          element._attributes[classAttr] = classAttribute; 
62 |         }
63 |       } else {
64 |         element.className = classAttribute;
65 |       }
66 |     }
67 |   }
68 | 
69 |   return setClass;
70 | });
71 | 


--------------------------------------------------------------------------------
/src/query/var/OBSERVABLE.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return '__blocks.observable__';
3 | });


--------------------------------------------------------------------------------
/src/query/var/classAttr.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return 'class';
3 | });


--------------------------------------------------------------------------------
/src/query/var/dataIdAttr.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return 'data-id';
3 | });


--------------------------------------------------------------------------------
/src/query/var/dataQueryAttr.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return 'data-query';
3 | });


--------------------------------------------------------------------------------
/src/query/var/parameterQueryCache.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return {};
3 | });


--------------------------------------------------------------------------------
/src/query/var/queries.js:
--------------------------------------------------------------------------------
 1 | define([
 2 |   '../../core'
 3 | ], function (blocks) {
 4 |   /**
 5 |    * @typedef {Object} blocks.query
 6 |    * @property {boolean} [prioritize] - If set the query will be executed before all other queries on the element, except other prioritized quieries.
 7 |    * @property {boolean} [passRawValues] - If set observables will not be unwraped.
 8 |    * @property {boolean} [passDomQuery] - If set the current DomQuery will be passed as the first argument. Usefull to execute other queries on the element. See blocks.queries.if for an example.
 9 |    * @property {boolean} [supportsComments] - Specifies if the domQuery can be executed on comment nodes.
10 |    * @property {Object.<number, boolean>} [passRaw] - Specifies if an argument should be evaluated to it's value or passed as the string. The key is the index of the argument.
11 |    * @property {Function} [preprocess] - This function will be executed when the element is rendered the first time. The context ``this`` wil be the VirtualElement that get's rendered.
12 |    * @property {Function} [ready] - This function will be executed on the dom element when the element got rendered. The context ``this`` will be corresponding the dom node.
13 |    * @property {Function} [update] - This function will be executed on the dom element each time an observable passed as an argument changed. The context will be the corresponding dom node.
14 |    */
15 |   /**
16 |    * @type {Object<string, blocks.query>}
17 |    */
18 |   return (blocks.queries = {});
19 | });


--------------------------------------------------------------------------------
/src/query/var/virtualElementIdentity.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 | 	return '__blocks.VirtualElement__';
3 | });


--------------------------------------------------------------------------------
/src/var/hasOwn.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return Object.prototype.hasOwnProperty;
3 | });


--------------------------------------------------------------------------------
/src/var/identity.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return function (value) {
3 |     return value;
4 |   };
5 | });


--------------------------------------------------------------------------------
/src/var/slice.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return Array.prototype.slice;
3 | });


--------------------------------------------------------------------------------
/src/var/strundefined.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return typeof undefined;
3 | });
4 | 


--------------------------------------------------------------------------------
/src/var/support.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return {};
3 | });


--------------------------------------------------------------------------------
/src/var/toString.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return Object.prototype.toString;
3 | });


--------------------------------------------------------------------------------
/src/var/trimRegExp.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 |   return /^\s+|\s+$/gm;
3 | });


--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   //Do not use {a} as a constructor
 3 |   "-W053": false,
 4 | 
 5 |   "eqeqeq": false,
 6 |   "forin": false,
 7 |   "plusplus": false,
 8 |   "strict": false,
 9 |   "newcap": false,
10 | 
11 |   "es3": true,
12 |   "bitwise": true,
13 |   "camelcase": true,
14 |   "curly": true,
15 |   "freeze": true,
16 |   "immed": true,
17 |   "latedef": true,
18 |   "noarg": true,
19 |   "noempty": true,
20 |   "nonbsp": true,
21 |   "nonew": true,
22 |   "undef": true,
23 |   "unused": true,
24 |   "trailing": true,
25 |   "loopfunc": true,
26 |   "eqnull": true,
27 |   "quotmark": "single",
28 | 
29 |   "browser": true,
30 | 
31 |   "globals": {
32 |     "blocks": true,
33 |     "jQuery": true,
34 |     "
quot;: true,
35 | 
36 |     "setFixtures": true,
37 |     "runts": true,
38 |     "waits": true,
39 |     "beforeEach": true,
40 |     "afterEach": true,
41 |     "describe": true,
42 |     "expect": true,
43 |     "it": true,
44 |     "spyOn": true
45 |   }
46 | }
47 | 


--------------------------------------------------------------------------------
/test/Runner.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html>
 3 | <head>
 4 |     <meta charset="utf-8">
 5 |     <title>Jasmine Spec Runner v2.2.0</title>
 6 | 
 7 |     <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
 8 |     <link rel="stylesheet" href="runner-styles.css" />
 9 | 
10 |     <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
11 |     <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
12 |     <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
13 | 
14 |     <script src="../node_modules/jquery/dist/jquery.js"></script>
15 |     <script src="../node_modules/jasmine-jquery/lib/jasmine-jquery.js"></script>
16 | 
17 |     <!-- include source files here... -->
18 |     <script src="../dist/blocks.js"></script>
19 | 
20 |     <script src="blocks.testing.js"></script>
21 | 
22 |     <script>
23 |         blocks.testing.load();
24 |         blocks.debug.disable();
25 |     </script>
26 | </head>
27 | 
28 | <body>
29 | <div class='header'>
30 |     <div class='content'>
31 |         Run All Tests
32 |         <input class='run-all' type='button' value='Run' />
33 |     </div>
34 | </div>
35 | </body>
36 | </html>
37 | 


--------------------------------------------------------------------------------
/test/pages/input.html:
--------------------------------------------------------------------------------
 1 | <html>
 2 |   <head>
 3 |     <title></title>
 4 |     <link rel="stylesheet" href="styles.css" charset="utf-8">
 5 |     <script src="../../dist/blocks.js"></script>
 6 |     <script>
 7 |     blocks.query({
 8 |       value: blocks.observable('John Doe'),
 9 |       range: blocks.observable(3)
10 |     });
11 |     </script>
12 |   </head>
13 |   <body>
14 |     <div class="section">
15 |       <h3>Input, password, search, textarea and expressions update</h3>
16 | 
17 |       My name is: {{value}}
18 |       <div data-query="text(value)">
19 |       </div>
20 |       {{value}}
21 |       <br />
22 |       {{value}}
23 |       <br />
24 |       {{value}}
25 |       <br />
26 |       <input data-query="val(value)" />
27 |       <br />
28 |       <input type="password" data-query="val(value)" />
29 |       <br />
30 |       <input type="search" data-query="val(value)" />
31 |       <br />
32 |       <textarea data-query="val(value)">
33 |       </textarea>
34 |     </div>
35 | 
36 |     <div class="section">
37 |       <h3>Range</h3>
38 | 
39 |       {{range}}
40 |       <br />
41 |       <input data-query="val(range)" type="range" />
42 |     </div>
43 |   </body>
44 | </html>
45 | 


--------------------------------------------------------------------------------
/test/pages/parsing-each.html:
--------------------------------------------------------------------------------
 1 | <html>
 2 |   <head>
 3 |     <title></title>
 4 |     <script src="../../dist/blocks.js"></script>
 5 |     <script>
 6 |     blocks.query({
 7 |       items: blocks.observable([{
 8 | 
 9 |       }])
10 |     });
11 |     </script>
12 |   </head>
13 |   <body>
14 |     <table data-query="each(items)">
15 |       <tr data-query="each($root.columns)">
16 |         <td>
17 |           
18 |         </td>
19 |       </tr>
20 |     </table>
21 |   </body>
22 | </html>
23 | 


--------------------------------------------------------------------------------
/test/pages/parsing.html:
--------------------------------------------------------------------------------
  1 | <html>
  2 | 
  3 | <head>
  4 |   <title></title>
  5 |   <script src="../../dist/blocks.js"></script>
  6 |   <script>
  7 |     blocks.query({
  8 |       title: blocks.observable('The page have been successfully populated!'),
  9 |       afterContent: blocks.observable('THIS SHOULD NOT BE VISIBLE')
 10 |     });
 11 |   </script>
 12 | </head>
 13 | 
 14 | <body>
 15 |   <h1>{{title}}</h1>
 16 | 
 17 |   <hr />
 18 | 
 19 |   <script>
 20 |     var template = '{{here is a template}}';
 21 |     // <!-- some contents -->
 22 |     // <div data-query="each(don't foreach)">
 23 |     // /* */
 24 |   </script>
 25 | 
 26 |   <style>
 27 |     .styled:before {
 28 |       content: "This is before content! (which is \"'<escaped>'\")";
 29 |       display: block;
 30 |     }
 31 |     .styled {
 32 |       background: #00FF00;
 33 |       font-size: 22px;
 34 |     }
 35 |     .styled:after {
 36 |       content: "{{afterContent}}";
 37 |       display: block;
 38 |     }
 39 |   </style>
 40 |   <div class="styled">
 41 |     All this should be green and 22px.
 42 |   </div>
 43 | 
 44 |   <br />
 45 | 
 46 |   <div>
 47 |     Text &lt;content> should be scaped!
 48 |     <br /> quote - "
 49 |     <br /> single quote - '
 50 |     <br /> escaped quote - &quot;
 51 |     <br /> escaped single quote - &#39;
 52 |     <br /> ampersand - &
 53 |   </div>
 54 | 
 55 |   <h4>Parsing iframe with 'src' provided</h4>
 56 |   <iframe src="http://jsblocks.com"></iframe>
 57 |   <h4>Parsing iframe with 'src' provided and contents inside</h4>
 58 |   <iframe src="http://jsblocks.com">
 59 |     {{non existent field}}
 60 |   </iframe>
 61 |   <h4>Parsing iframe only with contents inside</h4>
 62 |   <iframe>
 63 |     {{non existent field}}
 64 |   </iframe>
 65 | 
 66 | 
 67 |   <br />
 68 |   <br />
 69 | 
 70 |   <h4>Parsing empty svg</h4>
 71 |   <svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"></svg>
 72 | 
 73 |   <h4>Parsing full svg</h4>
 74 |   <svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
 75 | 
 76 |     <rect width="100%" height="100%" fill="red" />
 77 | 
 78 |     <circle cx="150" cy="100" r="80" fill="green" />
 79 | 
 80 |     <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">{{svg}}</text>
 81 |   </svg>
 82 | 
 83 |   <br />
 84 |   <br />
 85 | 
 86 |   <h4>Parsing canvas element</h4>
 87 |   <canvas></canvas>
 88 |   <canvas id="canvas">
 89 |   </canvas>
 90 | 
 91 |   <h4>Parsing non-existant element type</h4>
 92 |   <nonexistent>
 93 |     {{title}}
 94 |   </nonexistent>
 95 |   <non-existent>
 96 |     {{title}}
 97 |   </non-existent>
 98 | 
 99 |   <noscript style="background:red;font-size:200px">
100 |     NB: THIS SHOULDN'T BE VISIBLE!!!
101 |   </noscript>
102 | 
103 |   <object data="move.swf" type="application/x-shockwave-flash">
104 |     <param name="foo" value="bar" />
105 |   </object>
106 | 
107 |   <audio>
108 |   </audio>
109 |   <video>
110 |   </video>
111 | 
112 | 
113 | 
114 |   <br />
115 |   <br />
116 |   <br />
117 |   <br />
118 | </body>
119 | 
120 | </html>
121 | 


--------------------------------------------------------------------------------
/test/pages/styles.css:
--------------------------------------------------------------------------------
 1 | .section {
 2 |   position: relative;
 3 |   padding: 50px;
 4 |   padding-top: 70px;
 5 |   border: 2px solid gray;
 6 |   margin-bottom: 50px;
 7 | }
 8 | 
 9 | .section h3 {
10 |   position: absolute;
11 |   top: 0;
12 | }
13 | 
14 | .mega-button {
15 |   margin: 0 auto;
16 |   padding: 20px;
17 |   margin: 40px;
18 | }


--------------------------------------------------------------------------------
/test/pages/two-way-data-binding.html:
--------------------------------------------------------------------------------
  1 | <!DOCTYPE html>
  2 | <html>
  3 | <head lang="en">
  4 |     <meta charset="UTF-8">
  5 |     <title></title>
  6 |     <link rel="stylesheet" href="styles.css"/>
  7 |     <script src="../../dist/blocks.js"></script>
  8 |     <script>
  9 |         var model = {
 10 |             text: blocks.observable("My name is John Doe."),
 11 |             initialTrue: blocks.observable(true),
 12 |             initialFalse: blocks.observable(false),
 13 |             number: blocks.observable("First"),
 14 |             numbers: blocks.observable(["First", "Second"]),
 15 |             items: blocks.observable(["Zero", "First", "Second", "Third", "Fourth"])
 16 |         };
 17 |         blocks.query(model);
 18 |     </script>
 19 | </head>
 20 | <body>
 21 |     <button class="mega-button">Change All Values Programatically</button>
 22 | 
 23 |     <div class="section">
 24 |         <h3>input[type="text]</h3>
 25 |         <input data-query="val(text)">
 26 |         <div data-query="each([$this])">
 27 |             <input data-query="val(text)">
 28 |         </div>
 29 |         {{text}}{{text}}
 30 |         <br />
 31 |         <span data-query="html(text)"></span><span data-query="text(text)"></span>
 32 |     </div>
 33 | 
 34 |     <div class="section">
 35 |         <h3>textarea</h3>
 36 |         <textarea data-query="val(text)"></textarea>
 37 |         <div data-query="each([$this, $this])">
 38 |             <textarea data-query="val(text)"></textarea>
 39 |         </div>
 40 |     </div>
 41 | 
 42 |     <div class="section">
 43 |         <h3>input[type="checkbox"]</h3>
 44 |         <input type="checkbox" data-query="checked(initialTrue)">
 45 |         <input type="checkbox" data-query="checked(initialFalse)">
 46 |         <div data-query="each([$this])">
 47 |             <input type="checkbox" data-query="checked(initialTrue)">
 48 |             <input type="checkbox" data-query="checked(initialFalse)">
 49 |         </div>
 50 |         {{initialTrue}}{{initialTrue}}
 51 |         <br>
 52 |         {{initialFalse}}{{initialFalse}}
 53 |     </div>
 54 | 
 55 |     <div class="section">
 56 |         <h3>input[type="radio"]</h3>
 57 |         <input type="radio" data-query="checked(initialTrue)">
 58 |         <input type="radio" data-query="checked(initialFalse)">
 59 |         <div data-query="each([$this])">
 60 |             <input type="radio" data-query="checked(initialTrue)">
 61 |             <input type="radio" data-query="checked(initialFalse)">
 62 |         </div>
 63 |         {{initialTrue}} {{initialTrue}}
 64 |         <br>
 65 |         {{initialFalse}} {{initialFalse}}
 66 |     </div>
 67 | 
 68 |     <div class="section">
 69 |         <h3>input[type="radio", value="specified"]</h3>
 70 |         <input type="radio" value="First" data-query="checked(number)">
 71 |         <input type="radio" value="Second" data-query="checked(number)">
 72 |         <input type="radio" value="Third" data-query="checked(number)">
 73 |         <div data-query="each([$this]">
 74 |             <input type="radio" value="First" data-query="checked(number)">
 75 |             <input type="radio" value="Second" data-query="checked(number)">
 76 |             <input type="radio" value="Third" data-query="checked(number)">
 77 |         </div>
 78 |         {{number}}
 79 |     </div>
 80 | 
 81 |     <div class="section">
 82 |         <h3>select[data-query="options()"]</h3>
 83 |         No update:
 84 |         <select data-query="options(items)">
 85 | 
 86 |         </select>
 87 |         <br>
 88 |         Update:
 89 |         <select data-query="options(items).val(number)">
 90 | 
 91 |         </select>
 92 |         <br>
 93 |         {{number}}
 94 |     </div>
 95 | 
 96 |     <div class="section">
 97 |         <h3>select[data-query="each()"]</h3>
 98 |         No update:
 99 |         <select data-query="each(items)">
100 |             <option data-query="val($this)">{{$this}}</option>
101 |         </select>
102 |         <br>
103 |         Update:
104 |         <select data-query="each(items).val(number)">
105 |             <option data-query="val($this)">{{$this}}</option>
106 |         </select>
107 |         <br />
108 |         Update (data-query=each()):
109 |         <div data-query="each([$this])">
110 |             <select data-query="each(items).val(number)">
111 |                 <option data-query="val($this)">{{$this}}</option>
112 |             </select>
113 |         </div>
114 |     </div>
115 | 
116 |     <div class="section">
117 |         <h3>select (created manually)</h3>
118 |         <select data-query="val(number)">
119 |             <option value="Zero">Zero</option>
120 |             <option value="First">First</option>
121 |             <option value="Second">Second</option>
122 |             <option value="Third">Third</option>
123 |             <option value="Fourth">Fourth</option>
124 |         </select>
125 |     </div>
126 | 
127 |     <div class="section">
128 |         <h3>select[multiple]</h3>
129 |         <select multiple="multiple" data-query="options(items).val(numbers)" style="float: left;height:100px;width:100px;">
130 | 
131 |         </select>
132 |         <select multiple="multiple" data-query="each(items).val(numbers)" style="float: left;height:100px;width:100px;">
133 |             <option data-query="val($this)">{{$this}}</option>
134 |         </select>
135 |         <ul data-query="each(numbers)" style="float: left;">
136 |             <li>{{$this}}</li>
137 |         </ul>
138 |         <div style="clear: both;"></div>
139 |     </div>
140 | 
141 |     <div class="section">
142 |         <h3>select (with optgroup)</h3>
143 |         <select>
144 | 
145 |         </select>
146 |     </div>
147 | </body>
148 | </html>


--------------------------------------------------------------------------------
/test/runner-styles.css:
--------------------------------------------------------------------------------
 1 | body, html {
 2 |     padding: 0;
 3 |     margin: 0;
 4 | }
 5 | 
 6 | .header {
 7 |     background: gray;
 8 | }
 9 | 
10 | .header .content {
11 |     display: inline-block;
12 |     margin: 0 auto;
13 |     font-size: 24px;
14 |     font-weight: bold;
15 |     padding: 5px;
16 | }
17 | 
18 | .specs, .specs ul {
19 |     list-style-type: none;
20 |     list-style-position: inside;
21 | }
22 | 
23 | .specs > li > ul.group {
24 |     float: left;
25 | }
26 | 
27 | .specs > li > ul > .group-title {
28 |     font-size: 24px;
29 | }
30 | 
31 | .specs input {
32 |     margin-left: 20px;
33 | }
34 | 
35 | .specs .line {
36 |     display: inline-block;
37 |     padding: 3px;
38 | }
39 | 
40 | .specs .selected .line {
41 |     background: #51b20a;
42 | }
43 | 
44 | .specs .group {
45 |     list-style-type: none;
46 | }
47 | 
48 | .specs .group-title {
49 |     font-weight: bold;
50 |     font-style: italic;
51 |     font-size: 18px;
52 | }
53 | 
54 | .specs .test {
55 |     list-style-type: circle;
56 | }


--------------------------------------------------------------------------------
/test/spec/dataSource/configuration.js:
--------------------------------------------------------------------------------
  1 | (function () {
  2 |   'use strict';
  3 |   /*global describe, blocks, it, expect */
  4 | 
  5 |   var testing = blocks.testing;
  6 | 
  7 |   describe('blocks.DataSource (Configuration) ->', function () {
  8 |     it('baseUrl url is appended to read data operation url', function () {
  9 |       var dataSource = new blocks.DataSource({
 10 |         baseUrl: 'base/',
 11 |         read: {
 12 |           url: 'read'
 13 |         }
 14 |       });
 15 | 
 16 |       var isRequestCalled = false;
 17 |       testing.overrideAjax({
 18 |         'base/read': function () {
 19 |           isRequestCalled = true;
 20 |         }
 21 |       });
 22 | 
 23 |       dataSource.read();
 24 |       expect(isRequestCalled).toBe(true);
 25 | 
 26 |       testing.restoreAjax();
 27 |     });
 28 | 
 29 |     it('baseUrl url is appended to read data operation url (when fetching)', function () {
 30 |       var dataSource = new blocks.DataSource({
 31 |         baseUrl: 'base/',
 32 |         read: {
 33 |           url: 'read'
 34 |         }
 35 |       });
 36 | 
 37 |       var isRequestCalled = false;
 38 |       testing.overrideAjax({
 39 |         'base/read': function () {
 40 |           isRequestCalled = true;
 41 |         }
 42 |       });
 43 | 
 44 |       dataSource.read();
 45 |       expect(isRequestCalled).toBe(true);
 46 | 
 47 |       testing.restoreAjax();
 48 |     });
 49 | 
 50 |     it('baseUrl url is appended to create operation url', function () {
 51 |       var dataSource = new blocks.DataSource({
 52 |         autoSync: true,
 53 |         baseUrl: 'base/',
 54 |         create: {
 55 |           url: 'create'
 56 |         }
 57 |       });
 58 | 
 59 |       var isRequestCalled = false;
 60 |       testing.overrideAjax({
 61 |         'base/create': function () {
 62 |           isRequestCalled = true;
 63 |         }
 64 |       });
 65 | 
 66 |       dataSource.data.add({
 67 |         FirstName: 'Antonio'
 68 |       });
 69 |       expect(isRequestCalled).toBe(true);
 70 | 
 71 |       testing.restoreAjax();
 72 |     });
 73 | 
 74 |     it('baseUrl url is appended to destroy operation url', function () {
 75 |       var dataSource = new blocks.DataSource({
 76 |         baseUrl: 'base/',
 77 |         destroy: {
 78 |           url: 'destroy'
 79 |         }
 80 |       });
 81 | 
 82 |       var isRequestCalled = false;
 83 |       testing.overrideAjax({
 84 |         'base/destroy': function () {
 85 |           isRequestCalled = true;
 86 |         }
 87 |       });
 88 | 
 89 |       dataSource.data.add({
 90 |         Id: 1
 91 |       });
 92 |       dataSource.sync();
 93 |       dataSource.data.remove(dataSource.data.first());
 94 |       dataSource.sync();
 95 |       expect(isRequestCalled).toBe(true);
 96 | 
 97 |       testing.restoreAjax();
 98 |     });
 99 | 
100 |     it('baseUrl url is appended to update operation url', function () {
101 |       var dataSource = new blocks.DataSource({
102 |         baseUrl: 'base/',
103 |         update: {
104 |           url: 'update'
105 |         }
106 |       });
107 | 
108 |       var isRequestCalled = false;
109 |       testing.overrideAjax({
110 |         'base/update': function () {
111 |           isRequestCalled = true;
112 |         }
113 |       });
114 | 
115 |       dataSource.data.add({
116 |         Id: 1
117 |       });
118 |       dataSource.update(1, {
119 |         FirstName: 'FirstName'
120 |       });
121 |       dataSource.sync();
122 |       expect(isRequestCalled).toBe(true);
123 | 
124 |       testing.restoreAjax();
125 |     });
126 | 
127 |     it('data could be set from configuration', function () {
128 |       var dataSource = new blocks.DataSource({
129 |         data: testing.createStaticData()
130 |       });
131 |       expect(dataSource.data().length).toBe(2);
132 |       expect(dataSource.data.first().FirstName).toBe('Antonio');
133 |     });
134 |   });
135 | })();


--------------------------------------------------------------------------------
/test/spec/mvc/application.js:
--------------------------------------------------------------------------------
 1 | (function () {
 2 |   var testing = blocks.testing;
 3 | 
 4 |   describe('blocks.Application: ', function () {
 5 |     var Application;
 6 |     beforeEach(function () {
 7 |       Application = blocks.Application();
 8 |       testing.overrideApplicationStart(Application);
 9 |     });
10 |     afterEach(function () {
11 |       blocks.core.deleteApplication();
12 |       //testing.restoreApplicationStart(Application);
13 |     });
14 | 
15 |     it('baseUrl is set correctly', function () {
16 |       blocks.core.deleteApplication();
17 |       Application = blocks.Application({
18 |         baseUrl: 'test'
19 |       });
20 |       testing.overrideApplicationStart(Application);
21 |       expect(Application.options.baseUrl).toBe('test');
22 |       expect(Application.options.history).toBe(true);
23 |     });
24 | 
25 |     it('propertyDefaultOptions exists', function () {
26 |       expect(Application.Property.Defaults).toBeDefined();
27 |     });
28 | 
29 |     it('options exists', function () {
30 |       expect(Application.options).toBeDefined();
31 |     });
32 | 
33 |     it('collectionDefaultOptions exists', function () {
34 |       expect(Application.Collection.Defaults).toBeDefined();
35 |     });
36 | 
37 |     it('viewDefaultOptions exists', function () {
38 |       expect(Application.View.Defaults).toBeDefined();
39 |     });
40 | 
41 |     it('application does not raise errors when start() is called more than once', function () {
42 |       Application.start();
43 |       Application.start();
44 |       Application.start();
45 |     });
46 |   });
47 | 
48 | })();
49 | 


--------------------------------------------------------------------------------
/test/spec/mvc/collection.js:
--------------------------------------------------------------------------------
  1 | (function () {
  2 |   var testing = blocks.testing;
  3 | 
  4 |   describe('blocks.Application.Collection: ', function () {
  5 |     var Application;
  6 |     var Products;
  7 |     var Remotes;
  8 |     var Product;
  9 | 
 10 |     beforeEach(function () {
 11 |       Application = blocks.Application();
 12 |       testing.overrideApplicationStart(Application);
 13 | 
 14 |       testing.overrideAjax({
 15 |         'Products': function () {
 16 |           return testing.createStaticData();
 17 |         }
 18 |       });
 19 | 
 20 |       Product = Application.Model({
 21 |         FirstName: Application.Property()
 22 |       });
 23 | 
 24 |       Products = Application.Collection(Product, {
 25 |         options: {
 26 |           read: {
 27 |             url: 'Products'
 28 |           },
 29 |           update: {
 30 | 
 31 |           },
 32 |           destroy: {
 33 | 
 34 |           },
 35 |           create: {
 36 | 
 37 |           }
 38 |         },
 39 | 
 40 |         customProperty: function () {
 41 |           return 'content';
 42 |         }
 43 |       });
 44 | 
 45 |       Remotes = Application.Collection({
 46 |         options: {
 47 |           read: {
 48 | 
 49 |           }
 50 |         }
 51 |       });
 52 | 
 53 |       Application.start();
 54 |     });
 55 | 
 56 |     afterEach(function () {
 57 |       blocks.core.deleteApplication();
 58 |       //testing.restoreApplicationStart(Application);
 59 |       testing.restoreAjax();
 60 |     });
 61 | 
 62 |     describe('read()', function () {
 63 |       it('returns the collection object', function () {
 64 |         var products = Products();
 65 |         expect(products.read()).toBe(products);
 66 |       });
 67 | 
 68 |       it('retrieves all data', function () {
 69 |         var products = Products();
 70 |         products.read();
 71 |         expect(products().length).toBe(2);
 72 |         expect(products.at(0).get('FirstName')).toBe('Antonio');
 73 |       });
 74 | 
 75 |       it('without calling read() data is not selected', function () {
 76 |         var products = Products();
 77 |         expect(products().length).toBe(0);
 78 |       });
 79 | 
 80 |       it('repopulates the array with the original items when called', function () {
 81 |         var products = Products();
 82 |         products.read();
 83 |         products.reset(products.filter(function (value) {
 84 |           return value.FirstName() != 'Antonio';
 85 |         }));
 86 |         products.read();
 87 |         expect(products().length).toBe(2);
 88 |         expect(products.at(0).get('FirstName')).toBe('Antonio');
 89 |       });
 90 | 
 91 |       it('changes are automatically cleared after read repopulation', function () {
 92 |         var products = Products();
 93 |         products.read();
 94 |         products.add({
 95 |           FirstName: 'Test'
 96 |         });
 97 |         expect(products.hasChanges()).toBe(true);
 98 | 
 99 |         products.read();
100 |         expect(products.hasChanges()).toBe(false);
101 |       });
102 | 
103 |       it('accepts parameters which are passed to the ajax request', function () {
104 | 
105 |       });
106 | 
107 |       it('accepts a callback function as last parameter after the additional parameters', function () {
108 | 
109 |       });
110 | 
111 |       it('accepts a callback as only parameter', function () {
112 | 
113 |       });
114 |     });
115 | 
116 |     describe('clearChanges()', function () {
117 |       it('returns the collection object', function () {
118 |         var products = Products();
119 |         expect(products.clearChanges()).toBe(products);
120 |       });
121 | 
122 |       it('calls dataSource.clearChanges()', function () {
123 |         // TODO: ....
124 |       });
125 |     });
126 | 
127 |     describe('hasChanges()', function () {
128 |       it('is observable', function () {
129 |         var products = Products();
130 |         expect(blocks.isObservable(products.hasChanges)).toBe(true);
131 |       });
132 | 
133 |       it('when no changes hasChanges returns false', function () {
134 |         var products = Products();
135 |         expect(products.hasChanges()).toBe(false);
136 |         products.read();
137 |         expect(products.hasChanges()).toBe(false);
138 |       });
139 | 
140 |       it('equals dataSource.hasChanges', function () {
141 |         var products = Products();
142 |         expect(products._dataSource.hasChanges).toBe(products.hasChanges);
143 |       });
144 |     });
145 | 
146 |     describe('sync()', function () {
147 |       it('returns the collection object', function () {
148 |         var products = Products();
149 |         expect(products.sync()).toBe(products);
150 |       });
151 | 
152 |       it('calls dataSource.sync() method', function () {
153 | 
154 |       });
155 |     });
156 | 
157 |     describe('reset()', function () {
158 |       it('reset returns the collection object', function () {
159 |         var products = Products();
160 |         expect(products.reset()).toBe(products);
161 |       });
162 | 
163 |       it('changes the collection successfully', function () {
164 |         var products = Products();
165 |         products.read();
166 |         expect(products().length).toBe(2);
167 |         products.reset(products.filter(function (value) {
168 |           return value.FirstName() != 'Antonio';
169 |         }));
170 |         expect(products().length).toBe(1);
171 |         expect(products.at(0).get('FirstName')).toBe('Mihaela');
172 |       });
173 | 
174 |       it('with no parameters clears the entire collection', function () {
175 |         var products = Products();
176 |         products.read();
177 |         products.reset();
178 |         expect(products().length).toBe(0);
179 |       });
180 |     });
181 | 
182 |     describe('add()', function () {
183 |       it('adding dataItems with different properties does not throw exception', function () {
184 |         var products = Products();
185 |         products.read();
186 | 
187 |         products.add({
188 |           NonExistentTillNowField: true
189 |         }, {
190 |           AnotherNonExistentFieldTillNow: true
191 |         });
192 |       });
193 |     });
194 | 
195 |     it('could initialize a initial data', function () {
196 |       var products = Products([{}, {}]);
197 |       expect(products().length).toBe(2);
198 |     });
199 | 
200 |     it('the initial data is converted to models', function () {
201 |       var products = Products([{ price: 1 }, { price: 2 }, { price: 3 }]);
202 |       var model = products()[0];
203 |       expect(blocks.isFunction(model.dataItem)).toBe(true);
204 |       expect(blocks.isFunction(model.validate)).toBe(true);
205 |       expect(blocks.isFunction(model.isNew)).toBe(true);
206 |       expect(blocks.isFunction(model.sync)).toBe(true);
207 |     });
208 | 
209 |     it('could initialize a initial data from Application.Collection.Product() type of initialization', function () {
210 |       var products = Products([{}, {}]);
211 |       expect(products().length).toBe(2);
212 |     });
213 | 
214 |     it('supports custom properties', function () {
215 |       var products = Products();
216 |       expect(products.customProperty()).toBe('content');
217 |     });
218 | 
219 |     it('methods from an observable are available', function () {
220 | 
221 |     });
222 | 
223 |     it('methods from an observable work correctly', function () {
224 | 
225 |     });
226 | 
227 |     it('methods from expressions are available', function () {
228 | 
229 |     });
230 | 
231 |     it('methods from expressions work correctly', function () {
232 | 
233 |     });
234 |   });
235 | })();
236 | 


--------------------------------------------------------------------------------
/test/spec/mvc/history.js:
--------------------------------------------------------------------------------
  1 | (function () {
  2 |   var testing = blocks.testing;
  3 | 
  4 |   function getCurrentUrl() {
  5 |     return window.location.href.substring(window.location.host.length);
  6 |   }
  7 | 
  8 |   describe('History', function () {
  9 |     var navigateChangesCount = 0;
 10 | 
 11 |     function getHistory(options) {
 12 |       var Application = blocks.Application({
 13 |         options: options
 14 |       });
 15 | 
 16 |       testing.overrideApplicationStart(Application);
 17 |       Application.start();
 18 |       var history = Application._history;
 19 |       blocks.core.deleteApplication();
 20 |       //testing.restoreApplicationStart(Application);
 21 | 
 22 |       $('#testElement').append($('<a>', { id: 'link' }));
 23 | 
 24 |       var navigate = history.navigate;
 25 |       history.navigate = function (fragment, options) {
 26 |         navigate.call(this, fragment, options);
 27 |         if (this._use == 'pushState' && (!options || !options.replace)) {
 28 |           navigateChangesCount++;
 29 |         }
 30 |       };
 31 | 
 32 |       history.dispose = function () {
 33 |         history._onUrlChanged = function () {
 34 |         };
 35 |       };
 36 | 
 37 |       return history;
 38 |     }
 39 | 
 40 |     function changeHash(url) {
 41 |       if (url.charAt(0) != '#') {
 42 |         url = '#' + url;
 43 |       }
 44 |       $('#link').attr('href', url)[0].click();
 45 |       navigateChangesCount++;
 46 |     }
 47 | 
 48 |     beforeEach(function () {
 49 |       navigateChangesCount = 0;
 50 |     });
 51 | 
 52 |     afterEach(function () {
 53 |       if (navigateChangesCount) {
 54 |         window.history.go(-navigateChangesCount);
 55 |       }
 56 |     });
 57 | 
 58 | 
 59 |     //describe('[hash]', function () {
 60 |     //    describe('navigate', function () {
 61 | 
 62 |     //    });
 63 | 
 64 |     //    describe('urlChange', function () {
 65 |     //        it('is event called', function () {
 66 |     //            var called = false;
 67 |     //            var history = getHistory();
 68 |     //            history.on('urlChange', function () {
 69 |     //                called = true;
 70 |     //            });
 71 | 
 72 |     //            changeHash('#newUrl');
 73 | 
 74 |     //            waits(100);
 75 | 
 76 |     //            runs(function () {
 77 |     //                expect(called).toBe(true);
 78 |     //                history.dispose();
 79 |     //            });
 80 |     //        });
 81 |     //    });
 82 |     //});
 83 | 
 84 |     //describe('[pushState]', function () {
 85 | 
 86 |     //    describe('navigate', function () {
 87 | 
 88 |     //    });
 89 | 
 90 |     //    describe('urlChange', function () {
 91 |     //        it('is called', function () {
 92 |     //            var history = getHistory({
 93 |     //                history: 'pushState'
 94 |     //            });
 95 |     //            var called = false;
 96 |     //            history.on('urlChange', function () {
 97 |     //                called = true;
 98 |     //            });
 99 |     //            history.navigate('/newUrl');
100 | 
101 |     //            waits(100);
102 | 
103 |     //            runs(function () {
104 |     //                expect(called).toBe(true);
105 |     //                history.dispose();
106 |     //            });
107 |     //        });
108 |     //    });
109 |     //});
110 |   });
111 | })();
112 | 


--------------------------------------------------------------------------------
/test/spec/mvc/view.js:
--------------------------------------------------------------------------------
 1 | (function () {
 2 |   var testing = blocks.testing;
 3 | 
 4 |   describe('blocks.Application.View: ', function () {
 5 |     var Application;
 6 |     var Product;
 7 |     var Products;
 8 | 
 9 |     beforeEach(function () {
10 |       Application = blocks.Application();
11 |       testing.overrideApplicationStart(Application);
12 | 
13 |       Product = Application.Model({
14 |         FirstName: Application.Property(),
15 |         LastName: Application.Property(),
16 |         Age: Application.Property()
17 |       });
18 | 
19 |       Products = Application.Collection(Product, {
20 | 
21 |       });
22 |     });
23 |     afterEach(function () {
24 |       blocks.core.deleteApplication();
25 |       //testing.restoreApplicationStart(Application);
26 |     });
27 | 
28 |     it('is active by default', function () {
29 |       Application.View('Products', {
30 | 
31 |       });
32 | 
33 |       $('#testElement').attr('data-query', 'text(Products.isActive)');
34 | 
35 |       Application.start();
36 | 
37 | 
38 |       expect($('#testElement')).toHaveText('true');
39 |     });
40 | 
41 |     it('correctly binds', function () {
42 |       Application.View('Products', {
43 |         init: function () {
44 |           this.products = Products(testing.createStaticData());
45 |         }
46 |       });
47 | 
48 |       $('#testElement').append($('<ul>', {
49 |         'data-query': 'each(Products.products)'
50 |       }).append($('<li>', {
51 |         'data-query': 'text(FirstName + City)'
52 |       })));
53 | 
54 |       Application.start();
55 | 
56 |       var $ul = $('#testElement').children();
57 |       expect($ul.children().length).toBe(2);
58 |       expect($ul.children().eq(0)).toHaveText('AntonioBlagoevgrad');
59 |       expect($ul.children().eq(1)).toHaveText('MihaelaSofia');
60 |     });
61 | 
62 |     it('creating View with nothing inside does not throw error', function () {
63 | 
64 |     });
65 | 
66 |     describe('blocks.queries.view', function () {
67 | 
68 |     });
69 |   });
70 | })();
71 | 


--------------------------------------------------------------------------------
/test/spec/query/extenders.js:
--------------------------------------------------------------------------------
  1 | (function () {
  2 | 
  3 |   describe('blocks.observable.skip', function () {
  4 |     it('using a primitive value', function () {
  5 |       var items = blocks.observable([1, 2, 3, 4, 5, 6]).extend('skip', 2);
  6 |       expect(items()).toEqual([1, 2, 3, 4, 5, 6]);
  7 |       expect(items.view()).toEqual([3, 4, 5, 6]);
  8 |     });
  9 | 
 10 |     it('using an observable', function () {
 11 |       var skipCount = blocks.observable(1);
 12 |       var items = blocks.observable([1, 2, 3, 4, 5, 6]).extend('skip', skipCount);
 13 |       expect(items()).toEqual([1, 2, 3, 4, 5, 6]);
 14 | 
 15 |       //expect(items.view()).toEqual([2, 3, 4, 5, 6]);
 16 | 
 17 |       skipCount(0);
 18 | 
 19 |       //expect(items.view()).toEqual([1, 2, 3, 4, 5, 6]);
 20 | 
 21 |       skipCount(2);
 22 | 
 23 |       expect(items.view()).toEqual([3, 4, 5, 6]);
 24 |     });
 25 | 
 26 |     it('observable correctly updates the skip', function () {
 27 |       var skipCount = blocks.observable(100);
 28 |       var items = blocks.observable([1, 2, 3, 4, 5, 6]).extend('skip', skipCount);
 29 |       expect(items()).toEqual([1, 2, 3, 4, 5, 6]);
 30 |       expect(items.view()).toEqual([]);
 31 | 
 32 |       skipCount(0);
 33 | 
 34 |       //expect(items.view()).toEqual([1, 2, 3, 4, 5, 6]);
 35 | 
 36 |       skipCount(100);
 37 | 
 38 |       expect(items.view()).toEqual([]);
 39 |     });
 40 |   });
 41 | 
 42 |   describe('blocks.observable.take', function () {
 43 |     it('using primitive value', function () {
 44 |       var items = blocks.observable([1, 2, 3, 4, 5, 6]).extend('take', 3);
 45 |       expect(items()).toEqual([1, 2, 3, 4, 5, 6]);
 46 |       expect(items.view()).toEqual([1, 2, 3]);
 47 |     });
 48 | 
 49 |     it('using observable and updates correctly', function () {
 50 |       var takeCount = blocks.observable(1);
 51 |       var items = blocks.observable([1, 2, 3, 4, 5, 6]).extend('take', takeCount);
 52 |       expect(items()).toEqual([1, 2, 3, 4, 5, 6]);
 53 |       //expect(items.view()).toEqual([1]);
 54 | 
 55 |       takeCount(0);
 56 | 
 57 |       //expect(items.view()).toEqual([]);
 58 | 
 59 |       takeCount(100);
 60 | 
 61 |       expect(items.view()).toEqual([1, 2, 3, 4, 5, 6]);
 62 |     });
 63 | 
 64 |     it('provide value bigger than the collection takes all the items', function () {
 65 |       var items = blocks.observable([1, 2, 3, 4, 5, 6]).extend('take', 100);
 66 |       expect(items()).toEqual([1, 2, 3, 4, 5, 6]);
 67 |       expect(items.view()).toEqual([1, 2, 3, 4, 5, 6]);
 68 |     });
 69 |   });
 70 | 
 71 |   describe('blocks.observable.sort', function () {
 72 |     // TODO: Default sorting should sort numbers correctly
 73 |     //it('default sorting', function () {
 74 |     //  var items = blocks.observable([5, 3, 7, 1, 9, 11, 2]).extend('sort');
 75 |     //  expect(items()).toEqual([5, 3, 7, 1, 9, 11, 2]);
 76 |     //  expect(items.view()).toEqual([1, 2, 3, 5, 7, 9, 11]);
 77 |     //});
 78 | 
 79 |     it('sort by field', function () {
 80 |       var data = [
 81 |         { id: 5 },
 82 |         { id: 3 },
 83 |         { id: 7 },
 84 |         { id: 1 }
 85 |       ];
 86 |       var items = blocks.observable(data).extend('sort', 'id');
 87 | 
 88 |       expect(items()).toEqual(data);
 89 |       expect(items.view()).toEqual([
 90 |         { id: 1 },
 91 |         { id: 3 },
 92 |         { id: 5 },
 93 |         { id: 7 }
 94 |       ]);
 95 |     });
 96 |   });
 97 | 
 98 |   describe('blocks.observable.filter', function () {
 99 |     it('filter default by providng observable', function () {
100 |       var filterValue = blocks.observable();
101 |       var items = blocks.observable([]).extend('filter', filterValue);
102 |     });
103 |   });
104 | })();


--------------------------------------------------------------------------------
/test/spec/query/public-methods.js:
--------------------------------------------------------------------------------
  1 | (function (undefined) {
  2 | 
  3 |   function initializeFixtures(tagName, attributes) {
  4 |     tagName = tagName || 'div';
  5 |     attributes = attributes || {};
  6 |     var fixture = $('<div>', {
  7 |       id: 'sandbox'
  8 |     });
  9 |     fixture.append($('<' + tagName + '>', blocks.extend({
 10 |       id: 'testElement'
 11 |     }, attributes)));
 12 |     setFixtures(fixture);
 13 |   }
 14 | 
 15 |   function query(model) {
 16 |     blocks.query(model || {}, document.getElementById('sandbox'));
 17 |   }
 18 | 
 19 |   describe('blocks.unwrapObservable()', function () {
 20 |     it('should return the same value when the value passed is not an observable', function () {
 21 |       var result = blocks.unwrapObservable(32);
 22 |       expect(result).toBe(32);
 23 |     });
 24 | 
 25 |     it('should return null when null is passed', function () {
 26 |       var result = blocks.unwrapObservable(null);
 27 |       expect(result).toBe(null);
 28 |     });
 29 | 
 30 |     it('should return undefined when undefined is passed', function () {
 31 |       var result = blocks.unwrapObservable(undefined);
 32 |       expect(result).toBe(undefined);
 33 |     });
 34 | 
 35 |     it('should return the observable value when an observable is passed', function () {
 36 |       var result = blocks.unwrapObservable(blocks.observable(0));
 37 |       expect(result).toBe(0);
 38 |     });
 39 | 
 40 |     it('should return the function result when the observable is a function', function () {
 41 |       var func = function () {
 42 |         return 0;
 43 |       },
 44 |           result = blocks.unwrapObservable(blocks.observable(func));
 45 |       expect(result).toBe(0);
 46 |     });
 47 | 
 48 |     it('should return empty string when the observable is empty string', function () {
 49 |       var result = blocks.unwrapObservable(blocks.observable(''));
 50 |       expect(result).toBe('');
 51 |     });
 52 | 
 53 |     it('should return null when the observable value is null', function () {
 54 |       var result = blocks.unwrapObservable(blocks.observable(null));
 55 |       expect(result).toBe(null);
 56 |     });
 57 | 
 58 |     it('should return undefined when the observable value is undefined', function () {
 59 |       var result = blocks.unwrapObservable(blocks.observable(undefined));
 60 |       expect(result).toBe(undefined);
 61 |     });
 62 | 
 63 |     it('should return the original object when __identity__ value is supplied', function () {
 64 |       var obj = { __identity__: 'observable' };
 65 |       var result = blocks.unwrapObservable(obj);
 66 |       expect(result).toBe(obj);
 67 |     });
 68 |   });
 69 | 
 70 |   describe('blocks.isObservable()', function () {
 71 |     it('isObservable() = true (Primitive types)', function () {
 72 |       expect(blocks.isObservable(blocks.observable(3))).toBe(true);
 73 |       expect(blocks.isObservable(blocks.observable(null))).toBe(true);
 74 |       expect(blocks.isObservable(blocks.observable(undefined))).toBe(true);
 75 |       expect(blocks.isObservable(blocks.observable(true))).toBe(true);
 76 |       expect(blocks.isObservable(blocks.observable(false))).toBe(true);
 77 |       expect(blocks.isObservable(blocks.observable(new Date()))).toBe(true);
 78 |       expect(blocks.isObservable(blocks.observable(new Object()))).toBe(true);
 79 |       expect(blocks.isObservable(blocks.observable({}))).toBe(true);
 80 |     });
 81 | 
 82 |     it('isObservable() = true (Dependency)', function () {
 83 |       expect(blocks.isObservable(blocks.observable(function () {
 84 |         return 1 + 3;
 85 |       }))).toBe(true);
 86 |     });
 87 | 
 88 |     it('isObservable() = true (Array)', function () {
 89 |       expect(blocks.isObservable(blocks.observable(new Array()))).toBe(true);
 90 |       expect(blocks.isObservable(blocks.observable([]))).toBe(true);
 91 |     });
 92 | 
 93 |     it('object is not an observable', function () {
 94 |       expect(blocks.isObservable({})).toBe(false);
 95 |       expect(blocks.isObservable(new Object())).toBe(false);
 96 |       expect(blocks.isObservable({
 97 |         __identity__: 'value',
 98 |         _isDependencyObservable: true
 99 |       })).toBe(false);
100 |     });
101 | 
102 |     it('array is not an observable', function () {
103 |       expect(blocks.isObservable([])).toBe(false);
104 |       expect(blocks.isObservable(new Array())).toBe(false);
105 |     });
106 | 
107 |     it('date is not an observable', function () {
108 |       expect(blocks.isObservable(new Date())).toBe(false);
109 |     });
110 | 
111 |     it('number is not an observable', function () {
112 |       expect(blocks.isObservable(3)).toBe(false);
113 |       expect(blocks.isObservable(3.14)).toBe(false);
114 |     });
115 | 
116 |     it('string is not an observable', function () {
117 |       expect(blocks.isObservable('')).toBe(false);
118 |       expect(blocks.isObservable(new String())).toBe(false);
119 |       expect(blocks.isObservable('__blocks.observable__')).toBe(false);
120 |     });
121 | 
122 |     it('passing null or undefined returns false', function () {
123 |       expect(blocks.isObservable(null)).toBe(false);
124 |       expect(blocks.isObservable(undefined)).toBe(false);
125 |     });
126 | 
127 |   });
128 | 
129 |   describe('blocks.dataItem()', function () {
130 |     it('returns null when null or undefined is passed', function () {
131 |       expect(blocks.dataItem(null)).toBe(null);
132 |       expect(blocks.dataItem(undefined)).toBe(null);
133 |     });
134 | 
135 |     it('returns null when nothing is passed', function () {
136 |       expect(blocks.dataItem()).toBe(null);
137 |     });
138 | 
139 |     it('returns null when empty plain object is passed', function () {
140 |       expect(blocks.dataItem({})).toBe(null);
141 |     });
142 | 
143 |     it('supports passing a jQuery object', function () {
144 |       initializeFixtures();
145 | 
146 |       $('#testElement').attr('data-query', 'with(context)');
147 | 
148 |       $('<div>').appendTo($('#testElement')).append($('<div class="testElement"></div>'));
149 | 
150 |       var context = {};
151 |       query({
152 |         context: context
153 |       });
154 | 
155 |       expect(blocks.dataItem($('.testElement'))).toBe(context);
156 |     });
157 |   });
158 | 
159 |   describe('blocks.context()', function () {
160 |     it('returns null when null or undefined is passed', function () {
161 |       expect(blocks.context(null)).toBe(null);
162 |       expect(blocks.context(undefined)).toBe(null);
163 |     });
164 | 
165 |     it('returns null when nothing is passed', function () {
166 |       expect(blocks.context()).toBe(null);
167 |     });
168 | 
169 |     it('returns null when empty plain object is passed', function () {
170 |       expect(blocks.context({})).toBe(null);
171 |     });
172 | 
173 |     it('supports passing a jQuery object', function () {
174 |       initializeFixtures();
175 | 
176 |       $('#testElement').attr('data-query', 'with(context)');
177 | 
178 |       $('<div>').appendTo($('#testElement')).append($('<div class="testElement"></div>'));
179 | 
180 |       var context = {};
181 |       query({
182 |         context: context
183 |       });
184 | 
185 |       expect(blocks.context($('.testElement')).$this).toBe(context);
186 |     });
187 |   });
188 | 
189 |   describe('blocks.domQuery()', function () {
190 | 
191 |   });
192 | })();


--------------------------------------------------------------------------------
/test/spec/query/queries.comments.js:
--------------------------------------------------------------------------------
 1 | ; (function () {
 2 |   testModule('blocks.queries.methodName', function (methodName) {
 3 |     function isPreprocess() {
 4 |       return methodName === 'preprocess';
 5 |     }
 6 | 
 7 |     function initializeFixtures(query, text) {
 8 |       query = query || '';
 9 |       var $fixture = $('<div>', {
10 |         id: 'sandbox'
11 |       });
12 |       var fixture = $fixture[0];
13 |       fixture.appendChild(document.createComment('blocks ' + query));
14 |       if (text) {
15 |         fixture.appendChild(document.createTextNode(text));
16 |       }
17 |       fixture.appendChild(document.createComment('/blocks'));
18 | 
19 |       setFixtures($fixture);
20 |     }
21 | 
22 |     function query(model) {
23 |       var queriesCache = {};
24 |       var query;
25 | 
26 |       if (methodName == 'update') {
27 |         for (query in blocks.queries) {
28 |           if (blocks.queries[query].update && !(query in { 'each': true, 'with': true, 'render': true })) {
29 |             queriesCache[query] = blocks.queries[query].preprocess;
30 |             blocks.queries[query].preprocess = null;
31 |           }
32 |         }
33 |       }
34 | 
35 |       blocks.query(model || {}, document.getElementById('sandbox'));
36 | 
37 |       if (methodName == 'update') {
38 |         for (query in blocks.queries) {
39 |           if (queriesCache[query] && !(query in { 'each': true, 'with': true, 'render': true })) {
40 |             blocks.queries[query].preprocess = queriesCache[query];
41 |           }
42 |         }
43 |       }
44 |     }
45 | 
46 |     function expectResult(value) {
47 |       expect($('#sandbox')[0].childNodes[1].nodeValue).toBe(value);
48 |     }
49 | 
50 |     describe('blocks.queries.define works on comments', function () {
51 | 
52 |     });
53 | 
54 |     describe('blocks.queries.with works on comments', function () {
55 | 
56 |     });
57 | 
58 |     describe('blocks.queries.render (comments)', function () {
59 | 
60 |     });
61 | 
62 |     describe('blocks.queries.if (comments)', function () {
63 | 
64 |     });
65 | 
66 |     describe('blocks.queries.ifnot (comments)', function () {
67 | 
68 |     });
69 | 
70 |     describe('blocks.quries.visible (comments)', function () {
71 | 
72 |     });
73 |   });
74 | })();


--------------------------------------------------------------------------------
/test/tests.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "blocks.dataSource": [
 3 |         "spec/dataSource/api.js",
 4 |         "spec/dataSource/configuration.js",
 5 |         "spec/dataSource/events.js"
 6 |     ],
 7 |     "blocks.mvc": [
 8 |         "spec/mvc/application.js",
 9 |         "spec/mvc/collection.js",
10 |         "spec/mvc/history.js",
11 |         "spec/mvc/model.js",
12 |         "spec/mvc/property.js",
13 |         "spec/mvc/router.js",
14 |         "spec/mvc/validation.js",
15 |         "spec/mvc/view.js"
16 |     ],
17 |     "blocks.query": [
18 |         "spec/query/attribute-queries.js",
19 |         "spec/query/contexts.js",
20 |         "spec/query/custom-queries.js",
21 |         "spec/query/element.js",
22 |         "spec/query/expressions.js",
23 |         "spec/query/extenders.js",
24 |         "spec/query/html-parsing.js",
25 |         "spec/query/observable.array.js",
26 |         "spec/query/observables.comments.js",
27 |         "spec/query/observables.dom.js",
28 |         "spec/query/observables.js",
29 |         "spec/query/public-methods.js",
30 |         "spec/query/queries.comments.js",
31 |         "spec/query/queries.js"
32 |     ]
33 | }


--------------------------------------------------------------------------------