├── .travis.yml
├── tests
├── utils
│ └── common-setup.js
├── .eslintrc
├── api-test.js
├── event-target-test.js
├── dom-test.js
├── context-test.js
├── dom-event-delegate-test.js
└── test-service-provider-test.js
├── examples
└── todo
│ ├── css
│ └── app.css
│ ├── bower_components
│ ├── todomvc-common
│ │ ├── bg.png
│ │ ├── base.js
│ │ └── base.css
│ └── jquery-1.11.1.min
│ │ └── .bower.json
│ ├── js
│ ├── app.js
│ ├── modules
│ │ ├── page.js
│ │ ├── header.js
│ │ ├── footer.js
│ │ └── list.js
│ ├── services
│ │ ├── todos-db.js
│ │ └── router.js
│ └── behaviors
│ │ └── todo.js
│ └── index.html
├── lib
├── wrap-start.partial
├── box.js
├── .eslintrc
├── wrap-end.partial
├── dom-native.js
├── dom-jquery.js
├── event-target.js
├── context.js
├── application-stub.js
├── test-service-provider.js
└── dom-event-delegate.js
├── .gitignore
├── config
├── copyright.txt
├── karma-conf.js
└── testing-utils-karma-conf.js
├── package.json
├── .eslintrc
├── CHANGELOG.md
├── dist
├── t3.min.js
├── t3-native.min.js
├── t3-jquery.min.js
├── t3-native-2.7.0.min.js
├── t3-jquery-2.7.0.min.js
├── t3.min.js.map
├── t3-jquery.min.js.map
└── t3-native.min.js.map
├── LICENSE
├── README.md
└── Makefile.js
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 |
5 |
--------------------------------------------------------------------------------
/tests/utils/common-setup.js:
--------------------------------------------------------------------------------
1 | /* eslint no-undef: 0, no-unused-vars: 0 */
2 |
3 | var assert = chai.assert;
4 |
--------------------------------------------------------------------------------
/examples/todo/css/app.css:
--------------------------------------------------------------------------------
1 | #clear-completed {
2 | display: none;
3 | }
4 | .has-completed-tasks #clear-completed {
5 | display: block;
6 | }
--------------------------------------------------------------------------------
/examples/todo/bower_components/todomvc-common/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/box/t3js/master/examples/todo/bower_components/todomvc-common/bg.png
--------------------------------------------------------------------------------
/lib/wrap-start.partial:
--------------------------------------------------------------------------------
1 | // Start wrapper
2 | // We use this to make sure we don't assign globals unless we actually want to
3 | (function(window) {
4 |
--------------------------------------------------------------------------------
/examples/todo/js/app.js:
--------------------------------------------------------------------------------
1 | // Since T3 exports Box as a namespace, assign a shortcut Application to Box.Application
2 | var Application = Box.Application;
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _site
2 | node_modules
3 | doc
4 | *.swp
5 | *.iml
6 | .idea
7 | .DS_Store
8 | npm-debug.log
9 | dist/t3-[0-9]*.js
10 | dist/t3-testing-[0-9]*.js
11 |
12 | # Karama generated test coverage
13 | coverage-client
14 | /.dir-locals.el
15 |
--------------------------------------------------------------------------------
/lib/box.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Base namespaces for Box JavaScript.
3 | * @author Box
4 | */
5 |
6 | /* eslint-disable no-unused-vars */
7 |
8 | /**
9 | * The one global object for Box JavaScript.
10 | * @namespace
11 | */
12 | var Box = {};
13 | /* eslint-enable no-unused-vars */
14 |
--------------------------------------------------------------------------------
/lib/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "brace-style": 2,
4 | "func-style": [2, "declaration"],
5 | "strict": [2, "function"],
6 | "guard-for-in": 2,
7 | "no-floating-decimal": 2,
8 | "no-underscore-dangle": 0,
9 | "no-nested-ternary": 2,
10 | "quotes": [2, "single"],
11 | "radix": 2,
12 | "wrap-iife": 2,
13 | "space-after-keywords": 2,
14 | "valid-jsdoc": [2, {
15 | "prefer": {
16 | "return": "returns"
17 | }
18 | }],
19 | // We allow unused args
20 | "no-unused-vars": [2, {"args": "none"}]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/todo/bower_components/jquery-1.11.1.min/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-1.11.1.min",
3 | "_cacheHeaders": {
4 | "ETag": "\"53628b86-1762a\"",
5 | "Last-Modified": "Thu, 01 May 2014 17:59:34 GMT",
6 | "Content-Length": "95786",
7 | "Content-Type": "application/x-javascript; charset=utf-8"
8 | },
9 | "_release": "e-tag:53628b86-",
10 | "main": "index.js",
11 | "_source": "http://code.jquery.com/jquery-1.11.1.min.js",
12 | "_target": "*",
13 | "_originalSource": "http://code.jquery.com/jquery-1.11.1.min.js",
14 | "_direct": true
15 | }
--------------------------------------------------------------------------------
/config/copyright.txt:
--------------------------------------------------------------------------------
1 | /*!
2 | Copyright 2016 Box, Inc. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
--------------------------------------------------------------------------------
/lib/wrap-end.partial:
--------------------------------------------------------------------------------
1 | if (typeof define === 'function' && define.amd) {
2 | // AMD
3 | define('t3', [], function() {
4 | return Box;
5 | });
6 | } else if (typeof module === 'object' && typeof module.exports === 'object') {
7 | // CommonJS/npm, we want to export Box instead of assigning to global Window
8 | module.exports = Box;
9 | } else {
10 | // Make sure not to override Box namespace
11 | window.Box = window.Box || {};
12 |
13 | // Copy all properties onto namespace (ES3 safe for loop)
14 | for (var key in Box) {
15 | if (Box.hasOwnProperty(key)) {
16 | window.Box[key] = Box[key];
17 | }
18 | }
19 | }
20 |
21 | // Potentially window is not defined yet, so bind to 'this' instead
22 | }(typeof window !== 'undefined' ? window : this));
23 | // End Wrapper
24 |
--------------------------------------------------------------------------------
/tests/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "window": false,
7 | "document": false,
8 | "Box": false,
9 | "$": false,
10 | "mocha": false,
11 | "chai": false,
12 | "sinon": false,
13 | "assert": true,
14 | "leche": true
15 | },
16 | "rules": {
17 | "brace-style": 2,
18 | "func-style": [2, "declaration"],
19 | "strict": [2, "function"],
20 | "guard-for-in": 2,
21 | "no-floating-decimal": 2,
22 | "no-underscore-dangle": 0,
23 | "no-nested-ternary": 2,
24 | "quotes": [2, "single"],
25 | "radix": 2,
26 | "wrap-iife": 2,
27 | "space-after-keywords": 2,
28 | "valid-jsdoc": [2, {
29 | "prefer": {
30 | "return": "returns"
31 | }
32 | }],
33 | // We allow unused args
34 | "no-unused-vars": [2, {"args": "none"}]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/todo/js/modules/page.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Page Module
3 | * @author Box
4 | */
5 |
6 | /*
7 | * This module only handles routing on the page (via anchor links)
8 | */
9 | Application.addModule('page', function(context) {
10 |
11 | 'use strict';
12 |
13 | //--------------------------------------------------------------------------
14 | // Private
15 | //--------------------------------------------------------------------------
16 |
17 | var routerService;
18 |
19 | //--------------------------------------------------------------------------
20 | // Public
21 | //--------------------------------------------------------------------------
22 |
23 | return {
24 |
25 | /**
26 | * Initializes the module. Caches a data store object to todos
27 | * @returns {void}
28 | */
29 | init: function() {
30 | var baseUrl = context.getGlobal('location').pathname;
31 |
32 | routerService = context.getService('router');
33 | routerService.init([
34 | baseUrl,
35 | baseUrl + 'active',
36 | baseUrl + 'completed'
37 | ]);
38 | },
39 |
40 | /**
41 | * Destroys the module.
42 | * @returns {void}
43 | */
44 | destroy: function() {
45 | routerService.destroy();
46 | }
47 |
48 | };
49 |
50 | });
51 |
--------------------------------------------------------------------------------
/examples/todo/bower_components/todomvc-common/base.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | if (location.hostname === 'todomvc.com') {
5 | window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
6 | }
7 |
8 | function getSourcePath() {
9 | // If accessed via tastejs.github.io/todomvc/, strip the project path.
10 | if (location.hostname.indexOf('github.io') > 0) {
11 | return location.pathname.replace(/todomvc\//, '');
12 | }
13 | return location.pathname;
14 | }
15 |
16 | function appendSourceLink() {
17 | var sourceLink = document.createElement('a');
18 | var paragraph = document.createElement('p');
19 | var footer = document.getElementById('info');
20 | var urlBase = 'https://github.com/tastejs/todomvc/tree/gh-pages';
21 |
22 | if (footer) {
23 | sourceLink.href = urlBase + getSourcePath();
24 | sourceLink.appendChild(document.createTextNode('Check out the source'));
25 | paragraph.appendChild(sourceLink);
26 | footer.appendChild(paragraph);
27 | }
28 | }
29 |
30 | function redirect() {
31 | if (location.hostname === 'tastejs.github.io') {
32 | location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com');
33 | }
34 | }
35 |
36 | appendSourceLink();
37 | redirect();
38 | })();
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "t3js",
3 | "description": "T3 Javascript Framework",
4 | "version": "2.7.0",
5 | "author": "Box (https://www.box.com/)",
6 | "files": [
7 | "LICENSE",
8 | "README.md",
9 | "dist"
10 | ],
11 | "bugs": "http://github.com/box/t3js/issues",
12 | "homepage": "http://t3js.org",
13 | "license": "Apache-2.0",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/box/t3js"
17 | },
18 | "scripts": {
19 | "test": "node Makefile.js test",
20 | "test-watch": "node Makefile.js test-watch",
21 | "lint": "node Makefile.js lint",
22 | "dist": "node Makefile.js dist",
23 | "patch": "node Makefile.js patch",
24 | "minor": "node Makefile.js minor",
25 | "major": "node Makefile.js major"
26 | },
27 | "main": "dist/t3-native.js",
28 | "devDependencies": {
29 | "assertive-chai": "^1.0.2",
30 | "dateformat": "^1.0.11",
31 | "eslint": "^1.1.0",
32 | "jquery": "^1.11.1",
33 | "karma": "^0.12.31",
34 | "karma-coverage": "^0.2.7",
35 | "karma-mocha": "^0.1.10",
36 | "karma-mocha-reporter": "^1.0.2",
37 | "karma-phantomjs-launcher": "^0.1.4",
38 | "karma-threshold-reporter": "^0.1.15",
39 | "leche": "^2.1.1",
40 | "mocha": "^2.2.1",
41 | "phantomjs": "^1.9.16",
42 | "semver": "^4.3.3",
43 | "shelljs": "^0.4.0",
44 | "shelljs-nodecli": "^0.1.1",
45 | "sinon": "^1.14.1",
46 | "uglify-js": "^2.4.17"
47 | },
48 | "keywords": [
49 | "javascript",
50 | "framework",
51 | "browser",
52 | "client-side",
53 | "modular",
54 | "component"
55 | ]
56 | }
57 |
--------------------------------------------------------------------------------
/tests/api-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tests for T3 API (package.json and dist files)
3 | * @author Box
4 | */
5 |
6 | /* eslint-env node */
7 | /* eslint strict: [2, "global"] */
8 |
9 | 'use strict';
10 |
11 | // @NOTE(nzakas): This file runs in Node.js, not Karma!
12 |
13 | //------------------------------------------------------------------------------
14 | // Requirements
15 | //------------------------------------------------------------------------------
16 |
17 | var assert = require('assertive-chai').assert,
18 | leche = require('leche'),
19 | path = require('path'),
20 | defaultT3 = require('../dist/t3'),
21 | nativeT3 = require('../dist/t3-native'),
22 | jqueryT3 = require('../dist/t3-jquery'),
23 | pkg = require('../package.json');
24 |
25 | //------------------------------------------------------------------------------
26 | // Tests
27 | //------------------------------------------------------------------------------
28 |
29 | describe('API', function() {
30 |
31 | describe('Defaults', function() {
32 | it('should use native DOM in default file', function() {
33 | assert.equal(defaultT3.DOM.type, 'native');
34 | });
35 |
36 | it('should use native DOM in native file', function() {
37 | assert.equal(nativeT3.DOM.type, 'native');
38 | });
39 |
40 | it('should use jQuery DOM in jQuery file', function() {
41 | assert.equal(jqueryT3.DOM.type, 'jquery');
42 | });
43 | });
44 |
45 | describe('Exports', function() {
46 |
47 | leche.withData({
48 | 'Default T3': defaultT3,
49 | 'Native T3': nativeT3,
50 | 'jQuery T3': jqueryT3
51 | }, function(T3) {
52 |
53 | leche.withData([
54 | 'DOM',
55 | 'DOMEventDelegate',
56 | 'EventTarget',
57 | 'Application',
58 | 'Context'
59 | ], function(name) {
60 | it('should have Box.' + name, function() {
61 | assert.isDefined(T3[name]);
62 | });
63 | });
64 |
65 | });
66 |
67 | });
68 |
69 | describe('package.json', function() {
70 |
71 | it('should export native T3', function() {
72 | assert.equal(require(path.join('../', pkg.main)), nativeT3);
73 | });
74 |
75 | });
76 |
77 | });
78 |
--------------------------------------------------------------------------------
/examples/todo/js/modules/header.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Header Module
3 | * @author Box
4 | */
5 |
6 | /*
7 | * Handles creation of new todo items
8 | */
9 | Application.addModule('header', function(context) {
10 |
11 | 'use strict';
12 |
13 | //--------------------------------------------------------------------------
14 | // Private
15 | //--------------------------------------------------------------------------
16 |
17 | var ENTER_KEY = 13;
18 |
19 | var todosDB;
20 |
21 | //--------------------------------------------------------------------------
22 | // Public
23 | //--------------------------------------------------------------------------
24 |
25 | return {
26 |
27 | /**
28 | * Initializes the module. Caches a data store object to todos
29 | * @returns {void}
30 | */
31 | init: function() {
32 | todosDB = context.getService('todos-db');
33 | },
34 |
35 | /**
36 | * Destroys the module.
37 | * @returns {void}
38 | */
39 | destroy: function() {
40 | todosDB = null;
41 | },
42 |
43 | /**
44 | * Handles the keydown event for the module.
45 | * @param {Event} event A DOM-normalized event object.
46 | * @param {HTMLElement} element The nearest HTML element with a data-type
47 | * attribute specified or null if there is none.
48 | * @param {string} elementType The value of data-type for the nearest
49 | * element with that attribute specified or null if there is none.
50 | * @returns {void}
51 | */
52 | onkeydown: function(event, element, elementType) {
53 |
54 | // code to be run when a click occurs
55 | if (elementType === 'new-todo-input') {
56 |
57 | if (event.keyCode === ENTER_KEY) {
58 |
59 | var todoTitle = (element.value).trim();
60 |
61 | if (todoTitle.length) {
62 | var newTodoId = todosDB.add(todoTitle);
63 |
64 | context.broadcast('todoadded', {
65 | id: newTodoId
66 | });
67 |
68 | // Clear input afterwards
69 | element.value = '';
70 | }
71 |
72 | event.preventDefault();
73 | event.stopPropagation();
74 | }
75 |
76 | }
77 |
78 | }
79 | };
80 |
81 | });
82 |
--------------------------------------------------------------------------------
/lib/dom-native.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview DOM abstraction to use native browser functionality to add and remove event listeners
3 | * in T3
4 | * @author jdivock
5 | */
6 |
7 |
8 | Box.NativeDOM = (function(){
9 | 'use strict';
10 |
11 | return {
12 |
13 | type: 'native',
14 |
15 | /**
16 | * Returns the first element that is a descendant of the element
17 | * on which it is invoked that matches the specified group of selectors.
18 | * @param {HTMLElement} root parent element to query off of
19 | * @param {string} selector query string to match on
20 | *
21 | * @returns {HTMLElement} first element found matching query
22 | */
23 | query: function(root, selector){
24 | return root.querySelector(selector);
25 | },
26 |
27 | /**
28 | * Returns a non-live NodeList of all elements descended from the
29 | * element on which it is invoked that match the specified group of CSS selectors.
30 | * @param {HTMLElement} root parent element to query off of
31 | * @param {string} selector query string to match on
32 | *
33 | * @returns {Array} elements found matching query
34 | */
35 | queryAll: function(root, selector){
36 | return root.querySelectorAll(selector);
37 | },
38 |
39 | /**
40 | * Adds event listener to element using native event listener
41 | * @param {HTMLElement} element Target to attach listener to
42 | * @param {string} type Name of the action to listen for
43 | * @param {function} listener Function to be executed on action
44 | *
45 | * @returns {void}
46 | */
47 | on: function(element, type, listener) {
48 | element.addEventListener(type, listener, false);
49 | },
50 |
51 | /**
52 | * Removes event listener to element using native event listener functions
53 | * @param {HTMLElement} element Target to remove listener from
54 | * @param {string} type Name of the action remove listener from
55 | * @param {function} listener Function to be removed from action
56 | *
57 | * @returns {void}
58 | */
59 | off: function(element, type, listener) {
60 | element.removeEventListener(type, listener, false);
61 | }
62 | };
63 | }());
64 |
65 | Box.DOM = Box.NativeDOM;
66 |
--------------------------------------------------------------------------------
/lib/dom-jquery.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview DOM abstraction to use jquery to add and remove event listeners
3 | * in T3
4 | * @author jdivock
5 | */
6 |
7 | /* eslint-env jquery */
8 |
9 | Box.JQueryDOM = (function() {
10 | 'use strict';
11 |
12 | return {
13 |
14 | type: 'jquery',
15 |
16 | /**
17 | * Returns the first element that is a descendant of the element
18 | * on which it is invoked that matches the specified group of selectors.
19 | * @param {HTMLElement} root parent element to query off of
20 | * @param {string} selector query string to match on
21 | *
22 | * @returns {HTMLElement} first element found matching query
23 | */
24 | query: function(root, selector) {
25 | // Aligning with native which returns null if not found
26 | return jQuery(root).find(selector)[0] || null;
27 | },
28 |
29 | /**
30 | * Returns a non-live NodeList of all elements descended from the
31 | * element on which it is invoked that match the specified group of CSS selectors.
32 | * @param {HTMLElement} root parent element to query off of
33 | * @param {string} selector query string to match on
34 | *
35 | * @returns {Array} elements found matching query
36 | */
37 | queryAll: function(root, selector) {
38 | return jQuery.makeArray(jQuery(root).find(selector));
39 | },
40 |
41 | /**
42 | * Adds event listener to element via jquery
43 | * @param {HTMLElement} element Target to attach listener to
44 | * @param {string} type Name of the action to listen for
45 | * @param {function} listener Function to be executed on action
46 | *
47 | * @returns {void}
48 | */
49 | on: function(element, type, listener) {
50 | jQuery(element).on(type, listener);
51 | },
52 |
53 | /**
54 | * Removes event listener to element via jquery
55 | * @param {HTMLElement} element Target to remove listener from
56 | * @param {string} type Name of the action remove listener from
57 | * @param {function} listener Function to be removed from action
58 | *
59 | * @returns {void}
60 | */
61 | off: function(element, type, listener) {
62 | jQuery(element).off(type, listener);
63 | }
64 | };
65 | }());
66 |
67 | Box.DOM = Box.JQueryDOM;
68 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "window": false,
4 | "document": false,
5 | "Box": true,
6 | "$": false
7 | },
8 | "extends": "eslint:recommended",
9 | "rules": {
10 | "no-alert": 2,
11 | "no-array-constructor": 2,
12 | "no-caller": 2,
13 | "no-catch-shadow": 2,
14 | "no-empty-label": 2,
15 | "no-eval": 2,
16 | "no-extend-native": 2,
17 | "no-extra-bind": 2,
18 | "no-extra-parens": [2, "functions"],
19 | "no-floating-decimal": 2,
20 | "no-implied-eval": 2,
21 | "no-iterator": 2,
22 | "no-label-var": 2,
23 | "no-labels": 2,
24 | "no-lone-blocks": 2,
25 | "no-loop-func": 2,
26 | "no-multi-spaces": 2,
27 | "no-multi-str": 2,
28 | "no-native-reassign": 2,
29 | "no-nested-ternary": 2,
30 | "no-new": 2,
31 | "no-new-func": 2,
32 | "no-new-object": 2,
33 | "no-new-wrappers": 2,
34 | "no-octal-escape": 2,
35 | "no-process-exit": 2,
36 | "no-proto": 2,
37 | "no-return-assign": 2,
38 | "no-script-url": 2,
39 | "no-sequences": 2,
40 | "no-shadow": 2,
41 | "no-shadow-restricted-names": 2,
42 | "no-spaced-func": 2,
43 | "no-trailing-spaces": 2,
44 | "no-undef-init": 2,
45 | "no-underscore-dangle": 0,
46 | "no-unused-expressions": 2,
47 |
48 | // We allow unused args
49 | "no-unused-vars": [2, {"args": "none"}],
50 | "no-use-before-define": 2,
51 | "no-with": 2,
52 |
53 | "brace-style": 2,
54 | "camelcase": 2,
55 | "comma-spacing": 2,
56 | "consistent-return": 2,
57 | "curly": [2, "all"],
58 | "dot-notation": [2, { "allowKeywords": true }],
59 | "eol-last": 2,
60 | "eqeqeq": 2,
61 | "func-style": [2, "declaration"],
62 | "guard-for-in": 2,
63 | "indent": [2, "tab"],
64 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
65 | "new-cap": 2,
66 | "new-parens": 2,
67 | "quotes": [2, "single"],
68 | "radix": 2,
69 | "semi": 2,
70 | "semi-spacing": [2, {"before": false, "after": true}],
71 | "space-after-keywords": 2,
72 | "space-infix-ops": 2,
73 | "space-return-throw-case": 2,
74 | "space-unary-ops": [2, { "words": true, "nonwords": false }],
75 | "strict": [2, "function"],
76 | "wrap-iife": 2,
77 | "valid-jsdoc": [2, {
78 | "prefer": {
79 | "return": "returns"
80 | }
81 | }],
82 | "yoda": [2, "never"]
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/config/karma-conf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Karma configuration for T3 development.
3 | * @author nzakas
4 | */
5 |
6 | "use strict";
7 |
8 | //------------------------------------------------------------------------------
9 | // Requirements
10 | //------------------------------------------------------------------------------
11 |
12 | // None!
13 |
14 | //------------------------------------------------------------------------------
15 | // Public
16 | //------------------------------------------------------------------------------
17 |
18 | module.exports = function(config) {
19 |
20 | config.set({
21 |
22 | // base path that will be used to resolve all patterns (eg. files, exclude)
23 | basePath: '../',
24 |
25 | // frameworks to use
26 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
27 | frameworks: ['mocha'],
28 |
29 | // list of files / patterns to load in the browser
30 | files: [
31 | 'tests/utils/assert.js',
32 | 'node_modules/assertive-chai/assertive-chai.js',
33 | 'node_modules/sinon/pkg/sinon.js',
34 | 'node_modules/jquery/dist/jquery.js',
35 | 'node_modules/leche/dist/leche.js',
36 | 'tests/utils/common-setup.js',
37 | 'lib/box.js',
38 | 'lib/dom-native.js',
39 | 'lib/dom-jquery.js',
40 | 'lib/dom-event-delegate.js',
41 | 'lib/context.js',
42 | 'lib/event-target.js',
43 | 'lib/application.js',
44 | 'tests/*.js'
45 | ],
46 |
47 | // list of files to exclude
48 | exclude: [
49 | 'tests/api-test.js',
50 | 'tests/test-service-provider-test.js'
51 | ],
52 |
53 | // preprocess matching files before serving them to the browser
54 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
55 | preprocessors: {
56 | 'lib/*.js': ['coverage']
57 | },
58 |
59 | // web server port
60 | port: 9876,
61 |
62 | // start these browsers
63 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
64 | browsers: ['PhantomJS'],
65 |
66 | // enable / disable watching file and executing tests whenever any file changes
67 | autoWatch: false,
68 |
69 | // Continuous Integration mode
70 | // if true, Karma captures browsers, runs the tests and exits
71 | singleRun: true,
72 |
73 | // test results reporter to use
74 | // possible values: 'dots', 'progress'
75 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
76 | reporters: ['mocha', 'coverage', 'threshold'],
77 |
78 | // output HTML report of code coverage
79 | coverageReporter: {
80 | type: 'html',
81 | dir: 'coverage-client'
82 | },
83 |
84 | // set coverage limits
85 | thresholdReporter: {
86 | statements: 94,
87 | branches: 80,
88 | functions: 98,
89 | lines: 94
90 | },
91 |
92 | // output console.log calls to the console (default is false)
93 | captureConsole: true,
94 |
95 | // enable / disable colors in the output (reporters and logs)
96 | colors: true,
97 |
98 | // level of logging
99 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
100 | logLevel: config.LOG_INFO
101 | });
102 |
103 | };
104 |
--------------------------------------------------------------------------------
/tests/event-target-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tests for event-target
3 | * @author Box
4 | */
5 |
6 | describe('Box.EventTarget', function() {
7 |
8 | 'use strict';
9 |
10 | var sandbox = sinon.sandbox.create();
11 | var eventTarget;
12 |
13 | beforeEach(function() {
14 | eventTarget = new Box.EventTarget();
15 | });
16 |
17 | afterEach(function() {
18 | sandbox.verifyAndRestore();
19 | });
20 |
21 | describe('Multiple event handlers', function() {
22 |
23 | it('should be called for custom event', function() {
24 | eventTarget.on('myevent', sandbox.mock());
25 | eventTarget.on('myevent', sandbox.mock());
26 | eventTarget.fire('myevent');
27 | });
28 |
29 | it('should be prevented for duplicate handlers', function() {
30 | var callback = sandbox.mock();
31 | eventTarget.on('myevent', callback);
32 | eventTarget.on('myevent', callback);
33 | eventTarget.on('myevent', callback);
34 | eventTarget.fire('myevent');
35 | });
36 |
37 | });
38 |
39 | describe('Separate event handlers', function() {
40 |
41 | it('should be called for separate custom events', function() {
42 | eventTarget.on('myevent1', sandbox.mock());
43 | eventTarget.on('myevent2', sandbox.mock().never());
44 | eventTarget.fire('myevent1');
45 | });
46 |
47 | });
48 |
49 | describe('Event handler', function() {
50 |
51 | it('should be called for custom event', function() {
52 | eventTarget.on('myevent', sandbox.mock());
53 | eventTarget.fire('myevent');
54 | });
55 |
56 | it('should be called for custom event', function() {
57 | eventTarget.on('myevent', sandbox.mock());
58 | eventTarget.fire('myevent');
59 | });
60 |
61 | it('should be called with custom event object for custom event', function() {
62 | var handler = sandbox.mock().withArgs({
63 | type: 'myevent',
64 | data: undefined
65 | });
66 |
67 | eventTarget.on('myevent', handler);
68 | eventTarget.fire('myevent');
69 | });
70 |
71 | it('should be called with custom event object and extra data for custom event', function() {
72 | var handler = sandbox.mock().withArgs({
73 | type: 'myevent',
74 | data: {
75 | foo: 'bar',
76 | time: 'now'
77 | }
78 | });
79 |
80 | eventTarget.on('myevent', handler);
81 | eventTarget.fire('myevent', {
82 | foo: 'bar',
83 | time: 'now'
84 | });
85 | });
86 |
87 | it('should not be called for custom event after being removed', function() {
88 | var handler = sandbox.spy();
89 | eventTarget.on('myevent', handler);
90 |
91 | eventTarget.off('myevent', handler);
92 |
93 | eventTarget.fire('myevent');
94 | assert.ok(handler.notCalled);
95 | });
96 |
97 | it('should be called even after another event handler for the same type removes itself', function() {
98 | var handler1,
99 | handler2 = sandbox.spy();
100 |
101 | handler1 = function() {
102 | // this handler removes itself
103 | this.off('myevent', handler1);
104 | };
105 |
106 | eventTarget.on('myevent', handler1);
107 | eventTarget.on('myevent', handler2);
108 |
109 | eventTarget.fire('myevent');
110 | assert.ok(handler2.called);
111 | });
112 |
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/config/testing-utils-karma-conf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Karma configuration for T3 Testing Utilities
3 | * @author jtan
4 | */
5 |
6 | "use strict";
7 |
8 | //------------------------------------------------------------------------------
9 | // Requirements
10 | //------------------------------------------------------------------------------
11 |
12 | // None!
13 |
14 | //------------------------------------------------------------------------------
15 | // Public
16 | //------------------------------------------------------------------------------
17 |
18 | module.exports = function(config) {
19 |
20 | config.set({
21 |
22 | // base path that will be used to resolve all patterns (eg. files, exclude)
23 | basePath: '../',
24 |
25 | // frameworks to use
26 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
27 | frameworks: ['mocha'],
28 |
29 | // list of files / patterns to load in the browser
30 | files: [
31 | 'tests/utils/assert.js',
32 | 'node_modules/assertive-chai/assertive-chai.js',
33 | 'node_modules/sinon/pkg/sinon.js',
34 | 'node_modules/jquery/dist/jquery.js',
35 | 'node_modules/leche/dist/leche.js',
36 | 'tests/utils/common-setup.js',
37 | 'lib/box.js',
38 | 'lib/dom-native.js',
39 | 'lib/dom-jquery.js',
40 | 'lib/dom-event-delegate.js',
41 | 'lib/event-target.js',
42 | 'lib/application-stub.js',
43 | 'lib/test-service-provider.js',
44 | // Actual tests to run
45 | 'tests/test-service-provider-test.js',
46 | ],
47 |
48 | // list of files to exclude
49 | exclude: [
50 | 'tests/api-test.js'
51 | ],
52 |
53 | // preprocess matching files before serving them to the browser
54 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
55 | preprocessors: {
56 | 'lib/test-service-provider.js': ['coverage']
57 | },
58 |
59 | // web server port
60 | port: 9876,
61 |
62 | // start these browsers
63 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
64 | browsers: ['PhantomJS'],
65 |
66 | // enable / disable watching file and executing tests whenever any file changes
67 | autoWatch: false,
68 |
69 | // Continuous Integration mode
70 | // if true, Karma captures browsers, runs the tests and exits
71 | singleRun: true,
72 |
73 | // test results reporter to use
74 | // possible values: 'dots', 'progress'
75 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
76 | reporters: ['mocha', 'coverage', 'threshold'],
77 |
78 | // output HTML report of code coverage
79 | coverageReporter: {
80 | type: 'html',
81 | dir: 'coverage-client'
82 | },
83 |
84 | // set coverage limits
85 | thresholdReporter: {
86 | statements: 94,
87 | branches: 80,
88 | functions: 88,
89 | lines: 94
90 | },
91 |
92 | // output console.log calls to the console (default is false)
93 | captureConsole: true,
94 |
95 | // enable / disable colors in the output (reporters and logs)
96 | colors: true,
97 |
98 | // level of logging
99 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
100 | logLevel: config.LOG_INFO
101 | });
102 |
103 | };
104 |
--------------------------------------------------------------------------------
/examples/todo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | T3 • TodoNotMVC
7 |
8 |
9 |
10 |
11 |
50 |
55 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/event-target.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Definition of a custom event type. This is used as a utility
3 | * throughout the framework whenever custom events are used. It is intended to
4 | * be inherited from, either through the prototype or via mixin.
5 | * @author Box
6 | */
7 |
8 | Box.EventTarget = (function() {
9 |
10 | 'use strict';
11 |
12 | /**
13 | * An object that is capable of generating custom events and also
14 | * executing handlers for events when they occur.
15 | * @constructor
16 | */
17 | function EventTarget() {
18 |
19 | /**
20 | * Map of events to handlers. The keys in the object are the event names.
21 | * The values in the object are arrays of event handler functions.
22 | * @type {Object}
23 | * @private
24 | */
25 | this._handlers = {};
26 | }
27 |
28 | EventTarget.prototype = {
29 |
30 | // restore constructor
31 | constructor: EventTarget,
32 |
33 | /**
34 | * Adds a new event handler for a particular type of event.
35 | * @param {string} type The name of the event to listen for.
36 | * @param {Function} handler The function to call when the event occurs.
37 | * @returns {void}
38 | */
39 | on: function(type, handler) {
40 |
41 | var handlers = this._handlers[type],
42 | i,
43 | len;
44 |
45 | if (typeof handlers === 'undefined') {
46 | handlers = this._handlers[type] = [];
47 | }
48 |
49 | for (i = 0, len = handlers.length; i < len; i++) {
50 | if (handlers[i] === handler) {
51 | // prevent duplicate handlers
52 | return;
53 | }
54 | }
55 |
56 | handlers.push(handler);
57 | },
58 |
59 | /**
60 | * Fires an event with the given name and data.
61 | * @param {string} type The type of event to fire.
62 | * @param {Object} [data] An object with properties that should end up on
63 | * the event object for the given event.
64 | * @returns {void}
65 | */
66 | fire: function(type, data) {
67 |
68 | var handlers,
69 | i,
70 | len,
71 | event = {
72 | type: type,
73 | data: data
74 | };
75 |
76 | // if there are handlers for the event, call them in order
77 | handlers = this._handlers[event.type];
78 | if (handlers instanceof Array) {
79 | // @NOTE: do a concat() here to create a copy of the handlers array,
80 | // so that if another handler is removed of the same type, it doesn't
81 | // interfere with the handlers array during this loop
82 | handlers = handlers.concat();
83 | for (i = 0, len = handlers.length; i < len; i++) {
84 | handlers[i].call(this, event);
85 | }
86 | }
87 | },
88 |
89 | /**
90 | * Removes an event handler from a given event.
91 | * @param {string} type The name of the event to remove from.
92 | * @param {Function} handler The function to remove as a handler.
93 | * @returns {void}
94 | */
95 | off: function(type, handler) {
96 |
97 | var handlers = this._handlers[type],
98 | i,
99 | len;
100 |
101 | if (handlers instanceof Array) {
102 | for (i = 0, len = handlers.length; i < len; i++) {
103 | if (handlers[i] === handler) {
104 | handlers.splice(i, 1);
105 | break;
106 | }
107 | }
108 | }
109 | }
110 | };
111 |
112 | return EventTarget;
113 |
114 | }());
115 |
--------------------------------------------------------------------------------
/tests/dom-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tests for DOM abstraction layer
3 | * @author Box
4 | */
5 |
6 |
7 | describe('Box.DOM', function() {
8 | 'use strict';
9 |
10 | leche.withData({
11 | native: [Box.NativeDOM],
12 | jquery: [Box.JQueryDOM]
13 | }, function(dom) {
14 | var sandbox = sinon.sandbox.create();
15 |
16 | var testModule,
17 | nestedModule;
18 |
19 | before(function() {
20 | Box.DOM = dom;
21 | var fixture = document.createElement('div');
22 | fixture.id = 'mocha-fixture';
23 | document.body.appendChild(fixture);
24 | });
25 |
26 | beforeEach(function() {
27 | testModule = $('')[0];
28 | nestedModule = $('')[0];
29 | $('#mocha-fixture').append(testModule, nestedModule);
30 | });
31 |
32 | afterEach(function() {
33 | sandbox.verifyAndRestore();
34 |
35 | $('#mocha-fixture').empty();
36 | });
37 |
38 | after(function() {
39 | $('#mocha-fixture').remove();
40 | });
41 |
42 | describe('type', function() {
43 | it('should match type value of passed in event type', function() {
44 | assert.equal(Box.DOM.type, dom.type);
45 | });
46 | });
47 |
48 | describe('query()', function() {
49 | it('should return first element when multiples exist', function() {
50 | var testEl = Box.DOM.query(document.getElementById('mocha-fixture'), 'div');
51 |
52 | assert.equal(testEl, testModule);
53 | });
54 |
55 | it('should return null when no matches exist', function() {
56 | var testEl = Box.DOM.query(document.getElementById('mocha-fixture'), 'article');
57 |
58 | assert.isNull(testEl);
59 | });
60 |
61 | it('should return the element when exactly one match exists', function() {
62 | var testEl = Box.DOM.query(document.getElementById('mocha-fixture'), 'button');
63 | var testBtn = document.getElementById('module-target');
64 |
65 | assert.equal(testEl, testBtn);
66 | });
67 | });
68 |
69 | describe('queryAll()', function() {
70 | it('should return all elements when multiples exist', function() {
71 | var testEl = Box.DOM.queryAll(document.getElementById('mocha-fixture'), 'div');
72 |
73 | assert.equal(testEl.length, 3);
74 | });
75 |
76 | it('should return an empty array when no matches exist', function() {
77 | var testEl = Box.DOM.queryAll(document.getElementById('mocha-fixture'), 'article');
78 |
79 | assert.equal(testEl.length, 0);
80 | });
81 |
82 | it('should return an array of one element when exactly one match exists', function() {
83 | var testEl = Box.DOM.queryAll(document.getElementById('mocha-fixture'), 'button');
84 | var testBtn = document.getElementById('module-target');
85 |
86 | assert.equal(testEl.length, 1);
87 | assert.equal(testEl[0].id, testBtn.id);
88 | });
89 | });
90 |
91 | describe('on()', function() {
92 | it('should call an attached function', function() {
93 | var testBtn = document.getElementById('module-target');
94 | Box.DOM.on(testBtn, 'click', sandbox.mock());
95 |
96 | testBtn.click();
97 | });
98 | });
99 |
100 | describe('off()', function() {
101 | it('should not call a function when it\'s listener has been turned off', function() {
102 | var neverEver = sandbox.mock().never();
103 | var testBtn = document.getElementById('module-target');
104 | Box.DOM.on(testBtn, 'click', neverEver);
105 | Box.DOM.off(testBtn, 'click', neverEver);
106 |
107 | testBtn.click();
108 | });
109 | });
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/examples/todo/js/services/todos-db.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A todo object manager with built-in functionality
3 | * @author Box
4 | */
5 |
6 | /**
7 | * Todo Object
8 | * @typedef {Object} Todo
9 | * @property {number} id A unique identifier
10 | * @property {string} title A label for the todo
11 | * @property {boolean} completed Is the task complete?
12 | */
13 |
14 | /*
15 | * A todo object manager with built-in functionality
16 | */
17 | Application.addService('todos-db', function() {
18 |
19 | 'use strict';
20 |
21 | //--------------------------------------------------------------------------
22 | // Private
23 | //--------------------------------------------------------------------------
24 | /** @type {Object} */
25 | var todos = {};
26 |
27 | /** @type {number} */
28 | var counter = 0;
29 |
30 | //--------------------------------------------------------------------------
31 | // Public
32 | //--------------------------------------------------------------------------
33 |
34 | return {
35 |
36 | /**
37 | * Returns a Todo by id
38 | * @returns {Todo}
39 | */
40 | get: function(id) {
41 | return todos[id] || null;
42 | },
43 |
44 | /**
45 | * Returns list of all todos
46 | * @returns {Todo[]} List of todos
47 | */
48 | getList: function() {
49 | var todoList = [];
50 |
51 | Object.keys(todos).forEach(function(id) {
52 | todoList.push(todos[id]);
53 | });
54 |
55 | return todoList;
56 | },
57 |
58 | /**
59 | * Marks all todos as complete
60 | * @returns {void}
61 | */
62 | markAllAsComplete: function() {
63 | var me = this;
64 | Object.keys(todos).forEach(function(id) {
65 | me.markAsComplete(id);
66 | });
67 | },
68 |
69 | /**
70 | * Marks a todo as completed
71 | * @param {number} id The id of the todo
72 | * @returns {void}
73 | */
74 | markAsComplete: function(id) {
75 | if (todos[id]) {
76 | todos[id].completed = true;
77 | }
78 | },
79 |
80 | /**
81 | * Marks all todos as incomplete
82 | * @returns {void}
83 | */
84 | markAllAsIncomplete: function() {
85 | var me = this;
86 | Object.keys(todos).forEach(function(id) {
87 | me.markAsIncomplete(id);
88 | });
89 | },
90 |
91 | /**
92 | * Marks a todo as incomplete
93 | * @param {number} id The id of the todo
94 | * @returns {void}
95 | */
96 | markAsIncomplete: function(id) {
97 | if (todos[id]) {
98 | todos[id].completed = false;
99 | }
100 | },
101 |
102 | /**
103 | * Removes all completed tasks
104 | * @returns {void}
105 | */
106 | removeCompleted: function() {
107 | var me = this;
108 | Object.keys(todos).forEach(function(id) {
109 | if (todos[id].completed) {
110 | me.remove(id);
111 | }
112 | });
113 | },
114 |
115 | /**
116 | * Adds a todo
117 | * @param {string} title The label of the todo
118 | * @returns {number} The id of the new todo
119 | */
120 | add: function(title) {
121 | var todoId = counter++;
122 | todos[todoId] = {
123 | id: todoId,
124 | title: title,
125 | completed: false
126 | };
127 | return todoId;
128 | },
129 |
130 | /**
131 | * Edits a todo label
132 | * @param {number} id The unique identifier of the todo
133 | * @param {string} title The new label of the todo
134 | * @returns {void}
135 | */
136 | edit: function(id, title) {
137 | if (todos[id]) {
138 | todos[id].title = title;
139 | }
140 | },
141 |
142 | /**
143 | * Removes a todo by id
144 | * @param {number} id identifier of Todo to remove
145 | * @returns {void}
146 | */
147 | remove: function(id) {
148 | if (todos[id]) {
149 | delete todos[id];
150 | }
151 | }
152 | };
153 |
154 | });
155 |
--------------------------------------------------------------------------------
/lib/context.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Contains the Context type which is used by modules to interact
3 | * with the environment.
4 | * @author Box
5 | */
6 |
7 | Box.Context = (function() {
8 |
9 | 'use strict';
10 |
11 | /**
12 | * The object type that modules use to interact with the environment. Used
13 | * exclusively within Box.Application, but exposed publicly for easy testing.
14 | * @param {Box.Application} application The application object to wrap.
15 | * @param {HTMLElement} element Module's DOM element
16 | * @constructor
17 | */
18 | function Context(application, element) {
19 | this.application = application;
20 | this.element = element;
21 | }
22 |
23 | //-------------------------------------------------------------------------
24 | // Passthrough Methods
25 | //-------------------------------------------------------------------------
26 |
27 | Context.prototype = {
28 | constructor: Context,
29 |
30 | /**
31 | * Passthrough method to application that broadcasts messages.
32 | * @param {string} name Name of the message event
33 | * @param {*} [data] Custom parameters for the message
34 | * @returns {void}
35 | */
36 | broadcast: function(name, data) {
37 | this.application.broadcast(name, data);
38 | },
39 |
40 | /**
41 | * Passthrough method to application that retrieves services.
42 | * @param {string} serviceName The name of the service to retrieve.
43 | * @returns {Object|null} An object if the service is found or null if not.
44 | */
45 | getService: function(serviceName) {
46 | return this.application.getService(serviceName);
47 | },
48 |
49 | /**
50 | * Checks if a service exists
51 | * @param {string} serviceName The name of the service to check.
52 | * @returns {boolean} True, if service exist. False, otherwise.
53 | */
54 | hasService: function(serviceName) {
55 | return this.application.hasService(serviceName);
56 | },
57 |
58 | /**
59 | * Returns any configuration information that was output into the page
60 | * for this instance of the module.
61 | * @param {string} [name] Specific config parameter
62 | * @returns {*} config value or the entire configuration JSON object
63 | * if no name is specified (null if either not found)
64 | */
65 | getConfig: function(name) {
66 | return this.application.getModuleConfig(this.element, name);
67 | },
68 |
69 | /**
70 | * Returns a global variable
71 | * @param {string} name Specific global var name
72 | * @returns {*} returns the window-scope variable matching the name, null otherwise
73 | */
74 | getGlobal: function(name) {
75 | return this.application.getGlobal(name);
76 | },
77 |
78 | /**
79 | * Returns global configuration data
80 | * @param {string} [name] Specific config parameter
81 | * @returns {*} config value or the entire configuration JSON object
82 | * if no name is specified (null if either not found)
83 | */
84 | getGlobalConfig: function(name) {
85 | return this.application.getGlobalConfig(name);
86 | },
87 |
88 | /**
89 | * Passthrough method that signals that an error has occurred. If in development mode, an error
90 | * is thrown. If in production mode, an event is fired.
91 | * @param {Error} [exception] The exception object to use.
92 | * @returns {void}
93 | */
94 | reportError: function(exception) {
95 | this.application.reportError(exception);
96 | },
97 |
98 | //-------------------------------------------------------------------------
99 | // Service Shortcuts
100 | //-------------------------------------------------------------------------
101 |
102 | /**
103 | * Returns the element that represents the module.
104 | * @returns {HTMLElement} The element representing the module.
105 | */
106 | getElement: function() {
107 | return this.element;
108 | }
109 |
110 | };
111 |
112 | return Context;
113 |
114 | }());
115 |
--------------------------------------------------------------------------------
/lib/application-stub.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Fake application to use during testing
3 | * @author Box
4 | */
5 |
6 | (function() {
7 |
8 | 'use strict';
9 |
10 | /*
11 | * When testing actual Application, it should be included after to overwrite this stub.
12 | */
13 | Box.Application = (function() {
14 |
15 | var services = {},
16 | modules = {},
17 | behaviors = {};
18 |
19 | return {
20 |
21 | /**
22 | * Resets the application stub back to a clean state. Will also remove pre-registered components.
23 | * @returns {Box.Application} The application object.
24 | */
25 | reset: function() {
26 | services = {};
27 | modules = {};
28 | behaviors = {};
29 | return this;
30 | },
31 |
32 | /**
33 | * Registers a service to the application stub
34 | * @param {string} serviceName The name of the service
35 | * @param {Function} creator The service creator function
36 | * @returns {Box.Application} The application object.
37 | */
38 | addService: function(serviceName, creator) {
39 | services[serviceName] = {
40 | creator: creator
41 | };
42 | return this;
43 | },
44 |
45 | /**
46 | * Registers a module to the application stub
47 | * @param {string} moduleName The name of the module
48 | * @param {Function} creator The behavior creator function
49 | * @returns {Box.Application} The application object.
50 | */
51 | addModule: function(moduleName, creator) {
52 | modules[moduleName] = {
53 | creator: creator
54 | };
55 | return this;
56 | },
57 |
58 | /**
59 | * Registers a behavior to the application stub
60 | * @param {string} behaviorName The name of the behavior
61 | * @param {Function} creator The behavior creator function
62 | * @returns {Box.Application} The application object.
63 | */
64 | addBehavior: function(behaviorName, creator) {
65 | behaviors[behaviorName] = {
66 | creator: creator
67 | };
68 | return this;
69 | },
70 |
71 | /**
72 | * Checks if a service exists
73 | * @param {string} serviceName The name of the service to check.
74 | * @returns {boolean} True, if service exist. False, otherwise.
75 | */
76 | hasService: function(serviceName) {
77 | return services.hasOwnProperty(serviceName);
78 | },
79 |
80 | /**
81 | * Will create a new instance of a service with the given application context
82 | * @param {string} serviceName The name of the service being created
83 | * @param {Object} application The application context object (usually a TestServiceProvider)
84 | * @returns {?Object} The service object
85 | */
86 | getServiceForTest: function(serviceName, application) {
87 | var serviceData = services[serviceName];
88 | if (serviceData) {
89 | return services[serviceName].creator(application);
90 | }
91 | return null;
92 | },
93 |
94 | /**
95 | * Will create a new instance of a module with a given context
96 | * @param {string} moduleName The name of the module being created
97 | * @param {Object} context The context object (usually a TestServiceProvider)
98 | * @returns {?Object} The module object
99 | */
100 | getModuleForTest: function(moduleName, context) {
101 | var module = modules[moduleName].creator(context);
102 |
103 | if (!context.getElement) {
104 | // Add in a default getElement function that matches the first module element
105 | // Developer should stub this out if there are more than one instance of this module
106 | context.getElement = function() {
107 | return document.querySelector('[data-module="' + moduleName + '"]');
108 | };
109 | }
110 | return module;
111 | },
112 |
113 | /**
114 | * Will create a new instance of a behavior with a given context
115 | * @param {string} behaviorName The name of the behavior being created
116 | * @param {Object} context The context object (usually a TestServiceProvider)
117 | * @returns {?Object} The behavior object
118 | */
119 | getBehaviorForTest: function(behaviorName, context) {
120 | var behaviorData = behaviors[behaviorName];
121 | if (behaviorData) {
122 | // getElement on behaviors must be stubbed
123 | if (!context.getElement) {
124 | context.getElement = function() {
125 | throw new Error('You must stub `getElement` for behaviors.');
126 | };
127 | }
128 | return behaviors[behaviorName].creator(context);
129 | }
130 | return null;
131 | }
132 |
133 | };
134 |
135 | }());
136 |
137 | }());
138 |
--------------------------------------------------------------------------------
/examples/todo/js/modules/footer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Footer Module
3 | * @author Box
4 | */
5 |
6 | /*
7 | * Manages the footer module, including todo counts and filters
8 | */
9 | Application.addModule('footer', function(context) {
10 |
11 | 'use strict';
12 |
13 | //--------------------------------------------------------------------------
14 | // Private
15 | //--------------------------------------------------------------------------
16 |
17 | var todosDB,
18 | moduleEl;
19 |
20 | //--------------------------------------------------------------------------
21 | // Public
22 | //--------------------------------------------------------------------------
23 |
24 | return {
25 |
26 | messages: ['todoadded', 'todoremoved', 'todostatuschange', 'statechanged'],
27 |
28 | /**
29 | * Initializes the module. Caches a data store object to todos
30 | * @returns {void}
31 | */
32 | init: function() {
33 | todosDB = context.getService('todos-db');
34 | moduleEl = context.getElement();
35 | },
36 |
37 | /**
38 | * Destroys the module.
39 | * @returns {void}
40 | */
41 | destroy: function() {
42 | moduleEl = null;
43 | todosDB = null;
44 | },
45 |
46 | /**
47 | * Handles the click event for the module.
48 | * @param {Event} event A DOM-normalized event object.
49 | * @param {HTMLElement} element The nearest HTML element with a data-type
50 | * attribute specified or null if there is none.
51 | * @param {string} elementType The value of data-type for the nearest
52 | * element with that attribute specified or null if there is none.
53 | * @returns {void}
54 | */
55 | onclick: function(event, element, elementType) {
56 |
57 | // code to be run when a click occurs
58 | if (elementType === 'clear-btn') {
59 | this.clearCompletedTodos();
60 | }
61 |
62 | },
63 |
64 | /**
65 | * Handles all messages received for the module.
66 | * @param {string} name The name of the message received.
67 | * @param {*} [data] Additional data sent along with the message.
68 | * @returns {void}
69 | */
70 | onmessage: function(name, data) {
71 |
72 | switch(name) {
73 | case 'todoadded':
74 | case 'todoremoved':
75 | case 'todostatuschange':
76 | this.updateTodoCounts();
77 | break;
78 |
79 | case 'statechanged':
80 | this.updateSelectedFilterByUrl(data.url);
81 | break;
82 | }
83 | },
84 |
85 | /**
86 | * Updates the selected class on the filter links
87 | * @param {string} url The current url
88 | * @returns {void}
89 | */
90 | updateSelectedFilterByUrl: function(url) {
91 | var linkEls = moduleEl.querySelectorAll('a');
92 |
93 | for (var i = 0, len = linkEls.length; i < len; i++) {
94 | if (url === linkEls[i].pathname) {
95 | linkEls[i].classList.add('selected');
96 | } else {
97 | linkEls[i].classList.remove('selected');
98 | }
99 | }
100 | },
101 |
102 |
103 | /**
104 | * Updates todo counts based on what is in the todo DB
105 | * @returns {void}
106 | */
107 | updateTodoCounts: function() {
108 | var todos = todosDB.getList();
109 |
110 | var completedCount = 0;
111 | for (var i = 0, len = todos.length; i < len; i++) {
112 | if (todos[i].completed) {
113 | completedCount++;
114 | }
115 | }
116 |
117 | var itemsLeft = todos.length - completedCount;
118 |
119 | this.updateItemsLeft(itemsLeft);
120 | this.updateCompletedButton(completedCount);
121 |
122 | },
123 |
124 | /**
125 | * Updates the displayed count of incomplete tasks
126 | * @param {number} itemsLeft # of incomplete tasks
127 | * @returns {void}
128 | */
129 | updateItemsLeft: function(itemsLeft) {
130 | var itemText = itemsLeft === 1 ? 'item' : 'items';
131 |
132 | moduleEl.querySelector('.items-left-counter').textContent = itemsLeft;
133 | moduleEl.querySelector('.items-left-text').textContent = itemText + ' left';
134 | },
135 |
136 | /**
137 | * Updates the displayed count of completed tasks and hide/shows the button accordingly
138 | * @param {number} completedCount # of completed tasks
139 | * @returns {void}
140 | */
141 | updateCompletedButton: function(completedCount) {
142 | if (completedCount > 0) {
143 | moduleEl.querySelector('.completed-count').textContent = completedCount;
144 | moduleEl.classList.add('has-completed-tasks');
145 | } else {
146 | moduleEl.classList.remove('has-completed-tasks');
147 | }
148 | },
149 |
150 | /**
151 | * Removes any todos that have been completed
152 | * @returns {void}
153 | */
154 | clearCompletedTodos: function() {
155 | todosDB.removeCompleted();
156 | context.broadcast('todoremoved');
157 | }
158 |
159 | };
160 |
161 | });
162 |
--------------------------------------------------------------------------------
/tests/context-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tests for module context
3 | * @author Box
4 | */
5 |
6 | describe('Box.Context', function() {
7 |
8 | 'use strict';
9 |
10 | var sandbox = sinon.sandbox.create();
11 | var element = document.body;
12 |
13 | afterEach(function() {
14 | sandbox.verifyAndRestore();
15 | });
16 |
17 | describe('new Box.Context', function(){
18 | it('should have Box.Context as its constructor', function(){
19 | var context = new Box.Context();
20 |
21 | assert.strictEqual(context.constructor, Box.Context);
22 | });
23 | });
24 |
25 | describe('broadcast()', function() {
26 |
27 | it('should pass through to application when called', function() {
28 | var context,
29 | message = 'foo',
30 | application = {
31 | broadcast: function() {}
32 | };
33 |
34 | sandbox.mock(application).expects('broadcast').withArgs(message);
35 | context = new Box.Context(application, element);
36 | context.broadcast(message);
37 | });
38 |
39 | it('should pass through to application when called with arguments', function() {
40 | var context,
41 | message = 'foo',
42 | data = {},
43 | application = {
44 | broadcast: function() {}
45 | };
46 |
47 | sandbox.mock(application).expects('broadcast').withArgs(message, data);
48 | context = new Box.Context(application, element);
49 | context.broadcast(message, data);
50 | });
51 |
52 | });
53 |
54 | describe('getService()', function() {
55 |
56 | it('should pass through to application when called with service name', function() {
57 | var context,
58 | serviceName = 'foo',
59 | service = {},
60 | application = {
61 | getService: function() {}
62 | };
63 |
64 | sandbox.mock(application).expects('getService').withArgs(serviceName).returns(service);
65 | context = new Box.Context(application, element);
66 | assert.equal(context.getService(serviceName), service, 'getService() should return correct service');
67 | });
68 |
69 | });
70 |
71 | describe('hasService()', function() {
72 |
73 | it('should return true when Application has the service', function() {
74 | var context,
75 | serviceName = 'foo',
76 | application = {
77 | hasService: function() {}
78 | };
79 |
80 | sandbox.mock(application).expects('hasService').withArgs(serviceName).returns(true);
81 | context = new Box.Context(application, element);
82 | assert.isTrue(context.hasService(serviceName), 'hasService() should return true');
83 | });
84 |
85 | it('should return false when Application has the service', function() {
86 | var context,
87 | serviceName = 'foo',
88 | application = {
89 | hasService: function() {}
90 | };
91 |
92 | sandbox.mock(application).expects('hasService').withArgs(serviceName).returns(false);
93 | context = new Box.Context(application, element);
94 | assert.isFalse(context.hasService(serviceName), 'hasService() should return false');
95 | });
96 |
97 | });
98 |
99 | describe('getConfig()', function() {
100 |
101 | it('should pass through module element and config name to application.getModuleConfig() when called', function() {
102 | var context,
103 | config = {},
104 | application = {
105 | getModuleConfig: function() {}
106 | };
107 |
108 | sandbox.mock(application).expects('getModuleConfig').withArgs(element, 'foo').returns(config);
109 | context = new Box.Context(application, element);
110 | context.getConfig('foo');
111 | });
112 | });
113 |
114 | describe('getGlobal()', function() {
115 |
116 | it('should return the window-scope var when it exists', function () {
117 | var application = {
118 | getGlobal: function () {}
119 | };
120 |
121 | sandbox.stub(application, 'getGlobal').withArgs('foo').returns('bar');
122 |
123 | var context = new Box.Context(application, element);
124 | assert.strictEqual(context.getGlobal('foo'), 'bar', 'global var returned');
125 | });
126 |
127 | });
128 |
129 |
130 | describe('getGlobalConfig()', function() {
131 |
132 | it('should pass through to application when called', function() {
133 | var context,
134 | application = {
135 | getGlobalConfig: function() {}
136 | };
137 |
138 | sandbox.mock(application).expects('getGlobalConfig').withArgs('foo').returns('bar');
139 | context = new Box.Context(application, element);
140 | assert.equal(context.getGlobalConfig('foo'), 'bar', 'correct config value returned');
141 | });
142 |
143 | });
144 |
145 | describe('reportError()', function() {
146 |
147 | it('should pass through to application when called', function() {
148 | var context,
149 | application = {
150 | reportError: function() {}
151 | },
152 | exception = new Error('test error');
153 |
154 | sandbox.mock(application).expects('reportError').withArgs(exception);
155 | context = new Box.Context(application, element);
156 |
157 | context.reportError(exception);
158 | });
159 |
160 | });
161 |
162 |
163 | });
164 |
--------------------------------------------------------------------------------
/examples/todo/js/services/router.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A page routing service
3 | * @author Box
4 | */
5 |
6 | /*
7 | * A router for our objects
8 | */
9 | Application.addService('router', function(application) {
10 |
11 | 'use strict';
12 |
13 | var history = application.getGlobal('history');
14 | var pageRoutes;
15 |
16 | var regexRoutes = [],
17 | prevUrl;
18 |
19 | /**
20 | * Parses the user-friendly declared routes at the top of the application,
21 | * these could be init'ed or passed in from another context to help extract
22 | * navigation for T3 from the framework. Populates the regexRoutes variable
23 | * that is local to the service.
24 | * Regexs and parsing borrowed from Backbone's router since they did it right
25 | * and not inclined to make a new router syntax unless we have a reason to.
26 | * @returns {void}
27 | */
28 | function parseRoutes() {
29 | // Regexs to convert a route (/file/:fileId) into a regex
30 | var optionalParam = /\((.*?)\)/g,
31 | namedParam = /(\(\?)?:\w+/g,
32 | splatParam = /\*\w+/g,
33 | escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g,
34 | namedParamCallback = function(match, optional) {
35 | return optional ? match : '([^\/]+)';
36 | },
37 | route,
38 | regexRoute;
39 |
40 | for (var i = 0, len = pageRoutes.length; i < len; i++) {
41 | route = pageRoutes[i];
42 | regexRoute = route.replace(escapeRegExp, '\\$&')
43 | .replace(optionalParam, '(?:$1)?')
44 | .replace(namedParam, namedParamCallback)
45 | .replace(splatParam, '(.*?)');
46 | regexRoutes.push(new RegExp('^' + regexRoute + '$'));
47 | }
48 | }
49 |
50 | /**
51 | * Finds the regex route that matches the current fragment and returns
52 | * the matched route. This could eventually map back to the route it was
53 | * created from to return a smarter object (ie /file/:fileId returning {fileId: 42})
54 | * @param {string} fragment represents the current "URL" the user is getting to
55 | * @return {Object}
56 | */
57 | function matchGlob(fragment) {
58 | var regexRoute,
59 | globMatches;
60 |
61 | for (var idx in regexRoutes) {
62 | regexRoute = regexRoutes[idx];
63 |
64 | if (regexRoute.test(fragment)) {
65 | globMatches = fragment.match(regexRoute);
66 | }
67 | }
68 |
69 | return globMatches;
70 | }
71 |
72 | /**
73 | * Gets the fragment and broadcasts the previous URL and the current URL of
74 | * the page, ideally we wouldn't have a dependency on the previous URL...
75 | * @param {string} fragment the current "URL" the user is getting to
76 | * @returns {void}
77 | */
78 | function broadcastStateChanged(fragment) {
79 | var globMatches = matchGlob(fragment);
80 |
81 | if (!!globMatches) {
82 | application.broadcast('statechanged', {
83 | url: fragment,
84 | prevUrl: prevUrl,
85 | params: globMatches.splice(1) // Get everything after the URL
86 | });
87 | }
88 | }
89 |
90 | return {
91 | /**
92 | * The magical method the application code will call to navigate around,
93 | * high level it pushes the state, gets the templates from server, puts
94 | * them on the page and broadcasts the statechanged.
95 | * @param {Object} state the state associated with the URL
96 | * @param {string} title the title of the page
97 | * @param {string} fragment the current "URL" the user is getting to
98 | * @returns {void}
99 | */
100 | route: function(state, title, fragment) {
101 | prevUrl = history.state.hash;
102 |
103 | // First push the state
104 | history.pushState(state, title, fragment);
105 |
106 | // Then make the AJAX request
107 | broadcastStateChanged(fragment);
108 | },
109 |
110 | /**
111 | * Initialization that has to be done when the navigation service is required
112 | * by application code, sets up routes (which could be done later) and binds
113 | * to the popstate/hashchange events (which have to happen now).
114 | * @private
115 | * @param {string[]} routes An array of special route strings
116 | * @returns {void}
117 | */
118 | init: function(inputRoutes) {
119 | pageRoutes = inputRoutes || {};
120 |
121 | // Turn the routes globs into regular expressions...
122 | parseRoutes();
123 |
124 | var me = this;
125 |
126 | // Bind the document click listener to all anchor tags
127 | $(document).on('click.router', 'a', function(e) {
128 | // Append the query string to the end of the pathname
129 | var url = e.target.pathname + e.target.search;
130 |
131 | // Stop the event here and we can handle it
132 | e.preventDefault();
133 | e.stopPropagation();
134 |
135 | // Check the hash and the URL and make sure they aren't the same
136 | // and then navigate (in the case of an anchors href='#')
137 | if (url !== history.state.hash) {
138 | me.route({}, '', url);
139 | }
140 | });
141 |
142 | window.onpopupstate = function() {
143 | var state = history.state,
144 | url = state.hash;
145 |
146 | me.route({}, '', url);
147 | };
148 |
149 | history.pushState({}, '', application.getGlobal('location').pathname);
150 | },
151 |
152 | /**
153 | * Unbinds the event handlers on the DOM
154 | * @returns {void}
155 | */
156 | destroy: function() {
157 | $(document).off('click.router', 'a');
158 | }
159 | };
160 | });
161 |
162 |
--------------------------------------------------------------------------------
/tests/dom-event-delegate-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tests for DOMEventDelegate
3 | * @author Box
4 | */
5 |
6 |
7 | describe('Box.DOMEventDelegate', function() {
8 |
9 | 'use strict';
10 |
11 | leche.withData({
12 | native: [Box.NativeDOM],
13 | jquery: [Box.JQueryDOM]
14 | }, function(dom) {
15 |
16 | var sandbox = sinon.sandbox.create(),
17 | delegate;
18 |
19 | before(function() {
20 | Box.DOM = dom;
21 | var fixture = document.createElement('div');
22 | fixture.id = 'mocha-fixture';
23 | document.body.appendChild(fixture);
24 | });
25 |
26 | afterEach(function () {
27 | sandbox.verifyAndRestore();
28 |
29 | delegate = null;
30 | $('#mocha-fixture').empty();
31 | });
32 |
33 | after(function() {
34 | $('#mocha-fixture').remove();
35 | });
36 |
37 | /**
38 | * Use jQuery to click an element. Need to do this because PhantomJS
39 | * doesn't properly handle clicks otherwise.
40 | * @param {HTMLElement} element The element to click.
41 | * @returns {void}
42 | * @private
43 | */
44 | function click(element) {
45 | $(element).click();
46 | }
47 |
48 | describe('attachEvents()', function() {
49 |
50 | var testElement;
51 |
52 | beforeEach(function() {
53 | testElement = $('')[0];
54 | $('#mocha-fixture').append(testElement);
55 | });
56 |
57 | it('should respond to click when called with a click handler', function() {
58 |
59 | delegate = new Box.DOMEventDelegate(testElement, {
60 | onclick: sandbox.mock()
61 | });
62 |
63 | delegate.attachEvents();
64 |
65 | click(testElement.firstChild);
66 | });
67 |
68 | it('should pass three arguments to onclick when a data-type element is clicked', function() {
69 |
70 | delegate = new Box.DOMEventDelegate(testElement, {
71 | onclick: sandbox.mock().withExactArgs(
72 | sinon.match({ type: 'click' }),
73 | testElement.firstChild,
74 | 'submit'
75 | )
76 | });
77 |
78 | delegate.attachEvents();
79 |
80 | click(testElement.firstChild);
81 | });
82 |
83 | it('should pass three arguments to onclick when element with both data-type and data-module is clicked', function() {
84 |
85 | delegate = new Box.DOMEventDelegate(testElement, {
86 | onclick: sandbox.mock().withExactArgs(
87 | sinon.match({ type: 'click' }),
88 | testElement,
89 | 'form-type'
90 | )
91 | });
92 |
93 | delegate.attachEvents();
94 |
95 | testElement.setAttribute('data-type', 'form-type');
96 | click(document.querySelector('#non-typed-button'));
97 | });
98 |
99 | it('should pass three arguments to onclick when a non-data-type element is clicked', function() {
100 |
101 | delegate = new Box.DOMEventDelegate(testElement, {
102 | onclick: sandbox.mock().withExactArgs(
103 | sinon.match({ type: 'click' }),
104 | null,
105 | ''
106 | )
107 | });
108 |
109 | delegate.attachEvents();
110 |
111 | testElement.firstChild.removeAttribute('data-type');
112 | click(testElement.firstChild);
113 | });
114 |
115 | it('should pass three arguments to onclick when element has no ancestor with a data-type or data-module', function() {
116 |
117 | delegate = new Box.DOMEventDelegate(testElement, {
118 | onclick: sandbox.mock().withExactArgs(
119 | sinon.match({ type: 'click' }),
120 | null,
121 | ''
122 | )
123 | });
124 |
125 | delegate.attachEvents();
126 |
127 | testElement.removeAttribute('data-module');
128 | testElement.firstChild.removeAttribute('data-type');
129 | click(testElement.firstChild);
130 | });
131 |
132 | it('should respond to click only once when called twice', function() {
133 |
134 | delegate = new Box.DOMEventDelegate(testElement, {
135 | onclick: sandbox.mock() // enforces one call
136 | });
137 |
138 | delegate.attachEvents();
139 | delegate.attachEvents();
140 |
141 | click(testElement.firstChild);
142 | });
143 |
144 | it('should respond to custom events when a custom event type list is specified', function() {
145 |
146 | delegate = new Box.DOMEventDelegate(testElement, {
147 | ontouchstart: sandbox.mock()
148 | }, ['touchstart']);
149 |
150 | delegate.attachEvents();
151 |
152 | var event = document.createEvent('Event');
153 | event.initEvent('touchstart', true, true);
154 |
155 | testElement.firstChild.dispatchEvent(event);
156 | });
157 |
158 | it('should respond only to custom events when a custom event type list is specified', function() {
159 |
160 | delegate = new Box.DOMEventDelegate(testElement, {
161 | onclick: sandbox.mock().never()
162 | }, ['onsubmit']);
163 |
164 | delegate.attachEvents();
165 |
166 | click(testElement.firstChild);
167 | });
168 |
169 |
170 | });
171 |
172 | describe('detachEvents()', function() {
173 |
174 | it('should not respond to click when called with a click handler', function() {
175 | var testElement = $('')[0];
176 | $('#mocha-fixture').append(testElement);
177 |
178 | delegate = new Box.DOMEventDelegate(testElement, {
179 | onclick: sandbox.mock().never()
180 | });
181 |
182 | delegate.attachEvents();
183 | delegate.detachEvents();
184 | click(testElement.firstChild);
185 | });
186 |
187 | });
188 |
189 | });
190 | });
191 |
--------------------------------------------------------------------------------
/lib/test-service-provider.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A service provider that also contains a few pre-stubbed functions
3 | * @author Box
4 | */
5 |
6 | (function() {
7 |
8 | 'use strict';
9 |
10 | // We should use a reference directly the original application-stub object in case Box.Application gets stubbed out
11 | var application = Box.Application;
12 |
13 | // function stubs that are automatically included on a TestServiceProvider
14 | var APPLICATION_CONTEXT_STUBS = [
15 | // Shared between Application and Context
16 | 'broadcast', 'getGlobalConfig', 'reportError', 'reportWarning', 'reportInfo',
17 |
18 | // Application (only ones that should be called from a service)
19 | 'start', 'stop', 'startAll', 'stopAll', 'isStarted',
20 |
21 | // Context (module/behavior only) - getElement done separately
22 | 'getConfig'
23 | ];
24 |
25 | /**
26 | * Return a function stub that will throw an error if the test code does not properly mock out dependencies.
27 | * @param {string} method The name of the method being invoked
28 | * @returns {Function} A function stub
29 | */
30 | function functionStub(method) {
31 | /* eslint-disable no-extra-parens */
32 | return (function(methodKey) {
33 | return function() {
34 | throw new Error('Unexpected call to method "' + methodKey + '". You must stub this method out.');
35 | };
36 | }(method));
37 | /* eslint-enable no-extra-parens */
38 | }
39 |
40 | /**
41 | * Check if service is allowed. This should be replaced by Array.prototype.indexOf when we drop IE 8 support
42 | * @param {string} serviceName The name of the service being checked
43 | * @param {string[]} allowedServicesList The list of services allowed by the user
44 | * @returns {boolean} Returns true if service is in the allowed list
45 | * @private
46 | */
47 | function isServiceAllowed(serviceName, allowedServicesList) {
48 | for (var i = 0, len = allowedServicesList.length; i < len; i++) {
49 | if (allowedServicesList[i] === serviceName) {
50 | return true;
51 | }
52 | }
53 | return false;
54 | }
55 |
56 | /**
57 | * This object is used as a stub for application/context that is normally passed into services/modules/behaviors at create time.
58 | * It exposes the stubbed services passed in through the getService() method. Also allows the use of pre-registered services.
59 | * @param {Object} serviceStubs A map of service stubs
60 | * @param {string[]} [allowedServicesList] List of real services to allow access to
61 | * @constructor
62 | */
63 | Box.TestServiceProvider = function(serviceStubs, allowedServicesList) {
64 | this.stubs = serviceStubs || {};
65 | this.allowedServicesList = allowedServicesList || [];
66 | this.serviceInstances = {}; // Stores the instances of pre-registered services
67 | };
68 |
69 | Box.TestServiceProvider.prototype = {
70 | constructor: Box.TestServiceProvider,
71 |
72 | /**
73 | * Will retrieve either a service stub (prioritized) or the real service. Returns null if neither exists.
74 | * @param {string} serviceName The name of the service being retrieved
75 | * @returns {?Object} A service object or null if none exists
76 | * @throws {Error} Will throw an error if service does not exist
77 | */
78 | getService: function(serviceName) {
79 | var service = this.stubs[serviceName];
80 |
81 | // Return a service stub if found
82 | if (service) {
83 | return service;
84 | }
85 |
86 | // Check if this service is allowed to be pre-registered
87 | if (isServiceAllowed(serviceName, this.allowedServicesList)) {
88 | // Now check if we've already created the service instance
89 | if (this.serviceInstances.hasOwnProperty(serviceName)) {
90 | return this.serviceInstances[serviceName];
91 | } else {
92 | var preRegisteredService = application.getServiceForTest(serviceName, this);
93 | if (preRegisteredService) {
94 | // Save the instance for the next call to getService()
95 | this.serviceInstances[serviceName] = preRegisteredService;
96 | return preRegisteredService;
97 | } else {
98 | throw new Error('Service "' + serviceName + '" does not exist.');
99 | }
100 | }
101 |
102 | } else {
103 | throw new Error('Service "' + serviceName + '" is not on the `allowedServiceList`. Use "new Box.TestServiceProvider({ ...stubs... }, [\'' + serviceName + '\']);" or stub the service out.');
104 | }
105 | },
106 |
107 | /**
108 | * Checks if a service exists
109 | * @param {string} serviceName The name of the service to check.
110 | * @returns {boolean} True, if service exist. False, otherwise.
111 | */
112 | hasService: function(serviceName) {
113 | return this.stubs.hasOwnProperty(serviceName) || isServiceAllowed(serviceName, this.allowedServicesList) && application.hasService(serviceName);
114 | },
115 |
116 | /**
117 | * Retrieves a global var (this is the actual implementation for convenience in testing)
118 | * @param {string} name The name of the global
119 | * @returns {?*} The global object referenced or null if it does not exist
120 | */
121 | getGlobal: function(name) {
122 | if (name in window) {
123 | return window[name];
124 | } else {
125 | return null;
126 | }
127 | }
128 | };
129 |
130 | // Add stubbed functions onto prototype for testing convenience
131 | (function() {
132 | var stubName;
133 | for (var i = 0, len = APPLICATION_CONTEXT_STUBS.length; i < len; i++) {
134 | stubName = APPLICATION_CONTEXT_STUBS[i];
135 | Box.TestServiceProvider.prototype[stubName] = functionStub(stubName);
136 | }
137 | }());
138 |
139 | }());
140 |
--------------------------------------------------------------------------------
/tests/test-service-provider-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Tests for TestServiceProvider
3 | * @author Box
4 | */
5 |
6 | describe('Box.TestServiceProvider', function() {
7 |
8 | 'use strict';
9 |
10 | var sandbox = sinon.sandbox.create();
11 | var testServiceProvider;
12 |
13 | beforeEach(function() {
14 | Box.Application.reset();
15 | });
16 |
17 | afterEach(function() {
18 | sandbox.verifyAndRestore();
19 |
20 | Box.Application.reset();
21 | });
22 |
23 | describe('new Box.TestServiceProvider', function() {
24 |
25 | it('should have Box.TestServiceProvider as its constructor', function(){
26 | testServiceProvider = new Box.TestServiceProvider();
27 |
28 | assert.strictEqual(testServiceProvider.constructor, Box.TestServiceProvider);
29 | });
30 |
31 | it('should set this.stubs to serviceStubs when called', function() {
32 | var serviceStub = {
33 | bar: 'baz'
34 | };
35 | testServiceProvider = new Box.TestServiceProvider({
36 | foo: serviceStub
37 | });
38 |
39 | assert.propertyVal(testServiceProvider.stubs, 'foo', serviceStub);
40 | });
41 |
42 | it('should set allowedServicesList to this.allowedServicesList when called', function() {
43 | testServiceProvider = new Box.TestServiceProvider({}, ['example-service']);
44 |
45 | assert.deepEqual(testServiceProvider.allowedServicesList, ['example-service']);
46 | assert.deepEqual(testServiceProvider.serviceInstances, {});
47 | });
48 |
49 | });
50 |
51 | describe('getService()', function() {
52 |
53 | var stubbedService1 = { service: '1'},
54 | stubbedService2 = { service: '2'},
55 | preRegisteredService2 = { service: 'pre-2'},
56 | preRegisteredService3 = { service: 'pre-3' };
57 |
58 | beforeEach(function() {
59 | // Pre-register services
60 | var getServiceForTestStub = sandbox.stub(Box.Application, 'getServiceForTest');
61 | getServiceForTestStub.withArgs('service2').returns(preRegisteredService2);
62 | getServiceForTestStub.withArgs('service3').returns(preRegisteredService3);
63 | getServiceForTestStub.returns(null);
64 |
65 | testServiceProvider = new Box.TestServiceProvider({
66 | service1: stubbedService1,
67 | service2: stubbedService2
68 | }, ['service2', 'service3', 'service4']);
69 | });
70 |
71 | it('should return the stubbed version of a service when a stub is available', function() {
72 | assert.deepEqual(testServiceProvider.getService('service1'), stubbedService1);
73 | });
74 |
75 | it('should return the stubbed version of a service when a stub and a pre-registered service are available', function() {
76 | assert.deepEqual(testServiceProvider.getService('service2'), stubbedService2);
77 | });
78 |
79 | it('should return the pre-registered version of a service when a pre-registered service is available', function() {
80 | assert.deepEqual(testServiceProvider.getService('service3'), preRegisteredService3);
81 | });
82 |
83 | it('should return the same pre-registered service instance when called multiple times', function() {
84 | assert.deepEqual(testServiceProvider.getService('service3'), preRegisteredService3);
85 | assert.strictEqual(testServiceProvider.getService('service3'), testServiceProvider.getService('service3'));
86 | });
87 |
88 | it('should throw an error when service is not available and service is on the allowedServiceList', function() {
89 | assert.throws(function() {
90 | testServiceProvider.getService('service4');
91 | }, 'Service "not-available-service" does not exist.');
92 | });
93 |
94 | it('should throw an error when service is not available and service is not on the allowedServiceList', function() {
95 | assert.throws(function() {
96 | testServiceProvider.getService('not-available-service');
97 | }, 'Service "not-available-service" is not on the `allowedServiceList`. Use "new Box.TestServiceProvider({ ...stubs... }, [\'not-available-service\']);" or stub the service out.');
98 | });
99 |
100 | });
101 |
102 | describe('hasService()', function() {
103 |
104 | beforeEach(function() {
105 | // Setup Application
106 | var hasServiceStub = sandbox.stub(Box.Application, 'hasService');
107 | hasServiceStub.withArgs('service2').returns(true);
108 | hasServiceStub.withArgs('service4').returns(true);
109 | hasServiceStub.returns(false);
110 |
111 | testServiceProvider = new Box.TestServiceProvider({
112 | service1: {}
113 | }, ['service2', 'service3']);
114 | });
115 |
116 | it('should return false when service does not exist', function() {
117 | assert.isFalse(testServiceProvider.hasService('non-existent-service'));
118 | });
119 |
120 | it('should return true when service is stubbed', function() {
121 | assert.isTrue(testServiceProvider.hasService('service1'));
122 | });
123 |
124 | it('should return true when service is allowed and available', function() {
125 | assert.isTrue(testServiceProvider.hasService('service2'));
126 | });
127 |
128 | it('should return false when service is allowed but not available', function() {
129 | assert.isFalse(testServiceProvider.hasService('service3'));
130 | });
131 |
132 | it('should return false when service is available but not allowed', function() {
133 | assert.isFalse(testServiceProvider.hasService('service4'));
134 | });
135 |
136 | });
137 |
138 | describe('getGlobal()', function() {
139 |
140 | it('should return window var when requesting an existing property', function() {
141 | window.foo = 'bar';
142 | assert.strictEqual(testServiceProvider.getGlobal('foo'), window.foo);
143 | delete window.foo;
144 | });
145 |
146 | it('should return null when requesting a non-existent property', function() {
147 | assert.isNull(testServiceProvider.getGlobal('foobar'));
148 | });
149 |
150 | });
151 | });
152 |
--------------------------------------------------------------------------------
/lib/dom-event-delegate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview An object that encapsulates event delegation wireup for a
3 | * DOM element.
4 | * @author Box
5 | */
6 |
7 | Box.DOMEventDelegate = (function() {
8 |
9 | 'use strict';
10 |
11 | // Supported events for modules. Only events that bubble properly can be used in T3.
12 | var DEFAULT_EVENT_TYPES = ['click', 'mouseover', 'mouseout', 'mousedown', 'mouseup',
13 | 'mouseenter', 'mouseleave', 'mousemove', 'keydown', 'keyup', 'submit', 'change',
14 | 'contextmenu', 'dblclick', 'input', 'focusin', 'focusout'];
15 |
16 |
17 | /**
18 | * Determines if a given element represents a module.
19 | * @param {HTMLElement} element The element to check.
20 | * @returns {boolean} True if the element represents a module, false if not.
21 | * @private
22 | */
23 | function isModuleElement(element) {
24 | return element && element.hasAttribute('data-module');
25 | }
26 |
27 | /**
28 | * Determines if a given element represents a T3 type.
29 | * @param {HTMLElement} element The element to check.
30 | * @returns {boolean} True if the element represents a T3 type, false if not.
31 | * @private
32 | */
33 | function isTypeElement(element) {
34 | return element && element.hasAttribute('data-type');
35 | }
36 |
37 | /**
38 | * Finds the closest ancestor that of an element that has a data-type
39 | * attribute.
40 | * @param {HTMLElement} element The element to start searching from.
41 | * @returns {HTMLElement} The matching element or null if not found.
42 | */
43 | function getNearestTypeElement(element) {
44 | var found = false;
45 | var moduleBoundaryReached = false;
46 |
47 | // We need to check for the existence of 'element' since occasionally we call this on a detached element node.
48 | // For example:
49 | // 1. event handlers like mouseout may sometimes detach nodes from the DOM
50 | // 2. event handlers like mouseleave will still fire on the detached node
51 | // Checking existence of element.parentNode ensures the element is a valid HTML Element
52 | while (!found && element && element.parentNode && !moduleBoundaryReached) {
53 | found = isTypeElement(element);
54 | moduleBoundaryReached = isModuleElement(element);
55 |
56 | if (!found) {
57 | element = element.parentNode;
58 | }
59 |
60 | }
61 |
62 | return found ? element : null;
63 | }
64 |
65 | /**
66 | * Iterates over each supported event type that is also in the handler, applying
67 | * a callback function. This is used to more easily attach/detach all events.
68 | * @param {string[]} eventTypes A list of event types to iterate over
69 | * @param {Object} handler An object with onclick, onmouseover, etc. methods.
70 | * @param {Function} callback The function to call on each event type.
71 | * @param {Object} [thisValue] The value of "this" inside the callback.
72 | * @returns {void}
73 | * @private
74 | */
75 | function forEachEventType(eventTypes, handler, callback, thisValue) {
76 |
77 | var i,
78 | type;
79 |
80 | for (i = 0; i < eventTypes.length; i++) {
81 | type = eventTypes[i];
82 |
83 | // only call the callback if the event is on the handler
84 | if (handler['on' + type]) {
85 | callback.call(thisValue, type);
86 | }
87 | }
88 | }
89 |
90 | /**
91 | * An object that manages events within a single DOM element.
92 | * @param {HTMLElement} element The DOM element to handle events for.
93 | * @param {Object} handler An object containing event handlers such as "onclick".
94 | * @param {string[]} [eventTypes] A list of event types to handle (events must bubble). Defaults to a common set of events.
95 | * @constructor
96 | */
97 | function DOMEventDelegate(element, handler, eventTypes) {
98 |
99 | /**
100 | * The DOM element that this object is handling events for.
101 | * @type {HTMLElement}
102 | */
103 | this.element = element;
104 |
105 | /**
106 | * Object on which event handlers are available.
107 | * @type {Object}
108 | * @private
109 | */
110 | this._handler = handler;
111 |
112 | /**
113 | * List of event types to handle (make sure these events bubble!)
114 | * @type {string[]}
115 | * @private
116 | */
117 | this._eventTypes = eventTypes || DEFAULT_EVENT_TYPES;
118 |
119 | /**
120 | * Tracks event handlers whose this-value is bound to the correct
121 | * object.
122 | * @type {Object}
123 | * @private
124 | */
125 | this._boundHandler = {};
126 |
127 | /**
128 | * Indicates if events have been attached.
129 | * @type {boolean}
130 | * @private
131 | */
132 | this._attached = false;
133 | }
134 |
135 |
136 | DOMEventDelegate.prototype = {
137 |
138 | // restore constructor
139 | constructor: DOMEventDelegate,
140 |
141 | _handleEvent: function(event) {
142 | var targetElement = getNearestTypeElement(event.target),
143 | elementType = targetElement ? targetElement.getAttribute('data-type') : '';
144 |
145 | this._handler['on' + event.type](event, targetElement, elementType);
146 | },
147 |
148 | /**
149 | * Attaches all event handlers for the DOM element.
150 | * @returns {void}
151 | */
152 | attachEvents: function() {
153 | if (!this._attached) {
154 |
155 | forEachEventType(this._eventTypes, this._handler, function(eventType) {
156 | var that = this;
157 |
158 | function handleEvent() {
159 | that._handleEvent.apply(that, arguments);
160 | }
161 |
162 | Box.DOM.on(this.element, eventType, handleEvent);
163 |
164 | this._boundHandler[eventType] = handleEvent;
165 | }, this);
166 |
167 | this._attached = true;
168 | }
169 | },
170 |
171 | /**
172 | * Detaches all event handlers for the DOM element.
173 | * @returns {void}
174 | */
175 | detachEvents: function() {
176 | forEachEventType(this._eventTypes, this._handler, function(eventType) {
177 | Box.DOM.off(this.element, eventType, this._boundHandler[eventType]);
178 | }, this);
179 | }
180 | };
181 |
182 | return DOMEventDelegate;
183 | }());
184 |
185 |
--------------------------------------------------------------------------------
/examples/todo/js/modules/list.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Todo List
3 | * @author Box
4 | */
5 |
6 | /*
7 | * Manages the todo list which includes adding/removing/checking items
8 | */
9 | Application.addModule('list', function(context) {
10 |
11 | 'use strict';
12 |
13 | //--------------------------------------------------------------------------
14 | // Private
15 | //--------------------------------------------------------------------------
16 | var todosDB,
17 | moduleEl,
18 | listEl,
19 | filter;
20 |
21 | /**
22 | * Returns true if all todos in the list are complete
23 | * @returns {boolean}
24 | * @private
25 | */
26 | function isListCompleted() {
27 | var todos = todosDB.getList();
28 | var len = todos.length;
29 | var isComplete = len > 0;
30 | for (var i = 0; i < len; i++) {
31 | if (!todos[i].completed) {
32 | isComplete = false;
33 | break;
34 | }
35 | }
36 | return isComplete;
37 | }
38 |
39 | /**
40 | * Sets the todo filter based on URL
41 | * @returns {void}
42 | * @private
43 | */
44 | function setFilterByUrl(url) {
45 | if (url.indexOf('/active') > -1) {
46 | filter = 'incomplete';
47 | } else if (url.indexOf('/completed') > -1) {
48 | filter = 'complete';
49 | } else {
50 | filter = '';
51 | }
52 | }
53 |
54 | //--------------------------------------------------------------------------
55 | // Public
56 | //--------------------------------------------------------------------------
57 |
58 | return {
59 | /**
60 | * The behaviors that this module uses.
61 | * @type String[]
62 | */
63 | behaviors: ['todo'],
64 |
65 | /**
66 | * The messages that this modules listens for.
67 | * @type String[]
68 | */
69 | messages: ['todoadded', 'todoremoved', 'todostatuschange', 'statechanged'],
70 |
71 | /**
72 | * Initializes the module. Caches a data store object to todos
73 | * @returns {void}
74 | */
75 | init: function() {
76 | todosDB = context.getService('todos-db');
77 |
78 | moduleEl = context.getElement();
79 | listEl = moduleEl.querySelector('#todo-list');
80 | },
81 |
82 | /**
83 | * Destroys the module.
84 | * @returns {void}
85 | */
86 | destroy: function() {
87 | listEl = null;
88 | moduleEl = null;
89 | todosDB = null;
90 | },
91 |
92 | /**
93 | * Handles all click events for the module.
94 | * @param {Event} event A DOM-normalized event object.
95 | * @param {HTMLElement} element The nearest HTML element with a data-type
96 | * attribute specified or null if there is none.
97 | * @param {string} elementType The value of data-type for the nearest
98 | * element with that attribute specified or null if there is none.
99 | * @returns {void}
100 | */
101 | onchange: function(event, element, elementType) {
102 |
103 | if (elementType === 'select-all-checkbox') {
104 | var shouldMarkAsComplete = element.checked;
105 |
106 | if (shouldMarkAsComplete) {
107 | todosDB.markAllAsComplete();
108 | } else {
109 | todosDB.markAllAsIncomplete();
110 | }
111 |
112 | this.renderList();
113 |
114 | context.broadcast('todostatuschange');
115 | }
116 |
117 | },
118 |
119 | /**
120 | * Handles all messages received for the module.
121 | * @param {string} name The name of the message received.
122 | * @param {*} [data] Additional data sent along with the message.
123 | * @returns {void}
124 | */
125 | onmessage: function(name, data) {
126 |
127 | switch(name) {
128 | case 'todoadded':
129 | case 'todoremoved':
130 | this.renderList();
131 | this.updateSelectAllCheckbox();
132 | break;
133 |
134 | case 'todostatuschange':
135 | this.updateSelectAllCheckbox();
136 | break;
137 |
138 | case 'statechanged':
139 | setFilterByUrl(data.url);
140 | this.renderList();
141 | break;
142 | }
143 |
144 | },
145 |
146 | /**
147 | * Creates a list item for a todo based off of a template
148 | * @param {number} id The id of the todo
149 | * @param {string} title The todo label
150 | * @param {boolean} isCompleted Is todo complete
151 | * @returns {Node}
152 | */
153 | createTodo: function(id, title, isCompleted) {
154 | var todoTemplateEl = moduleEl.querySelector('.todo-template-container li'),
155 | newTodoEl = todoTemplateEl.cloneNode(true);
156 |
157 | // Set the label of the todo
158 | newTodoEl.querySelector('label').textContent = title;
159 | newTodoEl.setAttribute('data-todo-id', id);
160 | if (isCompleted) {
161 | newTodoEl.classList.add('completed');
162 | newTodoEl.querySelector('input[type="checkbox"]').checked = true;
163 | }
164 |
165 | return newTodoEl;
166 | },
167 |
168 | /**
169 | * Appends a new todo list element to the todo-list
170 | * @param {number} id The id of the todo
171 | * @param {string} title The todo label
172 | * @param {boolean} isCompleted Is todo complete
173 | * @returns {void}
174 | */
175 | addTodoItem: function(id, title, isCompleted) {
176 | listEl.appendChild(this.createTodo(id, title, isCompleted));
177 | },
178 |
179 | /**
180 | * Updates the 'checked' status of the select-all checkbox based on the status of the list
181 | * @returns {void}
182 | */
183 | updateSelectAllCheckbox: function() {
184 | var selectAllCheckboxEl = moduleEl.querySelector('#toggle-all');
185 | selectAllCheckboxEl.checked = isListCompleted();
186 | },
187 |
188 | /**
189 | * Removes all todo elements from the list (not necessarily from the db)
190 | * @returns {void}
191 | */
192 | clearList: function() {
193 | var todoListEl = moduleEl.querySelector('#todo-list');
194 | while (todoListEl.hasChildNodes()) {
195 | todoListEl.removeChild(todoListEl.lastChild);
196 | }
197 | },
198 |
199 | /**
200 | * Renders all todos in the todos db
201 | * @returns {void}
202 | */
203 | renderList: function() {
204 | // Clear the todo list first
205 | this.clearList();
206 |
207 | var todos = todosDB.getList(),
208 | todo;
209 |
210 | // Render todos - factor in the current filter as well
211 | for (var i = 0, len = todos.length; i < len; i++) {
212 | todo = todos[i];
213 |
214 | if (!filter
215 | || (filter === 'incomplete' && !todo.completed)
216 | || (filter === 'complete' && todo.completed)) {
217 | this.addTodoItem(todo.id, todo.title, todo.completed);
218 |
219 | }
220 | }
221 | }
222 | };
223 |
224 | });
225 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | v2.7.0 - August 26, 2016
2 |
3 | * 2.7.0 (Jeff Tan)
4 | * Issue #159: Added setErrorHandler to override default error handling (Rey)
5 |
6 | v2.6.0 - July 13, 2016
7 |
8 | * 2.6.0 (Jeff Tan)
9 | * Add test for reportInfo() method (Mike)
10 | * Added new method to report console info messages in debug mode (#175) (Mike)
11 | * Update README.md (Jeff Tan)
12 |
13 | v2.5.0 - May 20, 2016
14 |
15 | * 2.5.0 (Jeff Tan)
16 | * Fixes constructor reference in new instances of Box.Context and Box.TestServiceProvider (fixes #172) (Adam Platti)
17 |
18 | v2.4.0 - March 23, 2016
19 |
20 | * 2.4.0 (Jeff Tan)
21 | * Add reportWarning to test service provider (Jeff Tan)
22 | * Add custom event types (Jeff Tan)
23 | * Add reportWarning to Box.Application (Jeff Tan)
24 | * Include the data-module element when finding event targets (Jeff Tan)
25 | * Update Copyright year (Jeff Tan)
26 |
27 | v2.3.0 - March 16, 2016
28 |
29 | * 2.3.0 (Jeff Tan)
30 | * Set the handler context to the actual behavior or module instance (Justin Bennett)
31 |
32 | v2.2.0 - March 14, 2016
33 |
34 | * 2.2.0 (Jeff Tan)
35 | * Use recommended wording (Justin Bennett)
36 | * Remove bind, reword test (Justin Bennett)
37 | * Remove unnecessary `bind` call. (Zephraph)
38 | * Support cleaner onmessage handler (Zephraph)
39 | * Add malformed JSON error message (Zephraph)
40 |
41 | v2.1.0 - January 31, 2016
42 |
43 | * 2.1.0 (Jeff Tan)
44 | * Add basic element check to DOMEventDelegate (Jeff Tan)
45 | * Allow getModuleConfig() to execute before module instantiation (Jeff Tan)
46 |
47 | v2.0.2 - November 18, 2015
48 |
49 | * 2.0.2 (Jeff Tan)
50 | * Remove package.json caching in the dist function (Jeff Tan)
51 |
52 | v2.0.1 - November 18, 2015
53 |
54 | * 2.0.1 (Jeff Tan)
55 | * Fix release script to work with multiple dist calls (Jeff Tan)
56 | * Add debug output to release script (Jeff Tan)
57 |
58 | v2.0.0 - November 18, 2015
59 |
60 | * 2.0.0 (Jeff Tan)
61 | * Add separate file header for testing package (Jeff Tan)
62 | * Fixing spaces in build script (Jeff Tan)
63 | * Return singleton service instance when getService is called on pre-registered services (Jeff Tan)
64 | * Add mousemove to allowed event types (Jeff Tan)
65 | * Update README.md (Jeff Tan)
66 | * Remove service exports from T3 (Jeff Tan)
67 | * Update Readme for 2.0.0 release and add auto-version updating (Jeff Tan)
68 | * Add hasService() to context object (Jeff Tan)
69 | * [Breaking] Add allowedServiceList to TestServiceProvider (Jeff Tan)
70 | * Use jQuery instead of $ for dom event delegation (Jeff Tan)
71 | * Add linting to test directory (Jeff Tan)
72 | * Breaking: Initialize behaviors before module (Jeff Tan)
73 | * Change getService() to throw error when requesting non-existent service. Add hasService() method. (Jeff Tan)
74 | * Bind event handlers after init() is called (Jeff Tan)
75 | * Throw error when duplicate behaviors are included (Jeff Tan)
76 | * Revert "Check for circular dependencies only during instantiation of service" (Denis Rodin)
77 | * Check for circular dependencies only during instantiation of service (Denis Rodin)
78 | * Check for circular dependencies only during instantiation of service (Denis Rodin)
79 | * Breaking: Use NativeDOM by default (fixes #76) (Nicholas C. Zakas)
80 | * Build: Upgrade ESLint (fixes #90) (Nicholas C. Zakas)
81 |
82 | v1.5.1 - August 10, 2015
83 |
84 | * 1.5.1 (Nicholas C. Zakas)
85 | * Fix: Ensure DOMEventDelegate is in T3 release (Nicholas C. Zakas)
86 |
87 | v1.5.0 - August 5, 2015
88 |
89 | * 1.5.0 (Nicholas C. Zakas)
90 | * Update: Make Box.Application chainable (fixes #65) (Nicholas C. Zakas)
91 | * New: Add Box.DOMEventDelegate (fixes #47, fixes #63) (Nicholas C. Zakas)
92 | * Update email address for support (Nicholas C. Zakas)
93 |
94 | v1.4.1 - June 24, 2015
95 |
96 | * 1.4.1 (Jeff Tan)
97 | * Ammended existing test to cover new fields (Jason Divock)
98 | * Making errors a bit more reportable (Jason Divock)
99 |
100 | v1.4.0 - June 11, 2015
101 |
102 | * 1.4.0 (Jeff Tan)
103 | * Add missing commonJS and AMD wrappers (Jeff Tan)
104 |
105 | v1.3.0 - June 9, 2015
106 |
107 | * 1.3.0 (Jeff Tan)
108 | * Breaking out dom events into an abstraction layer, building different versions of t3 accordingly (Jason Divock)
109 | * Add AMD support. Fixes #50 (Priyajeet Hora)
110 | * Fix release script (Nicholas C. Zakas)
111 |
112 | v1.2.0 - April 23, 2015
113 |
114 | * 1.2.0 (Nicholas C. Zakas)
115 | * Change karma to use mocha reporter (Jeff Tan)
116 | * Fix embedded sourcemap URL (fixes #31) (Nicholas C. Zakas)
117 | * Add wrapper to T3 for CommonJS (Jeff Tan)
118 | * Remove event delegation on detached nodes (Jeff Tan)
119 | * Update: Fire event when broadcast() is called (fixes #43) (Nicholas C. Zakas)
120 | * Reverting dist changes (Priyajeet Hora)
121 | * Prevent event-target.js duplicate handlers. Fixes #35. (Priyajeet Hora)
122 | * Clean up duplicated assign code (fixes #29) (azu)
123 | * Todo example fails to update completed items. Fixes #25 (Priyajeet Hora)
124 | * Firefox innerText fixes as well as switching the select all checkbox logic to use the model data instead of view elements Fixes #21 (Priyajeet Hora)
125 | * readme grammar (Matthew Hadley)
126 | * Apply or remove completed class when select all is clicked. Fixes #18. (Priyajeet Hora)
127 | * Adding missing doctype Fixes #14 (Priyajeet Hora)
128 |
129 | v1.1.1 - April 14, 2015
130 |
131 | * 1.1.1 (Nicholas C. Zakas)
132 | * Update build script to publish to npm and update site (Nicholas C. Zakas)
133 | * Add Travis configuration (Nicholas C. Zakas)
134 | * Add sourcemap generation (Nicholas C. Zakas)
135 | * Update README with badges and more info (Nicholas C. Zakas)
136 | * Missing semicolon in application-stub.js Fixes #4 (Priyajeet Hora)
137 | * Create t3-testing bundle (Jeff Tan)
138 | * Updating Karam coverage dir and adding it to gitignore (Tarrence van As)
139 | * Add ESLint to package.json (Jeff Tan)
140 | * Add license and contribution guide (Nicholas C. Zakas)
141 | * Rename t3 to t3js (Jeff Tan)
142 | * Adding items to .npmignore. Fixes #60. (Priyajeet Hora)
143 | * Adding items to .npmignore. Fixes #60. (Priyajeet Hora)
144 | * Adding npmignore. Fixes #60 (Priyajeet Hora)
145 |
146 | v1.1.0 - March 30, 2015
147 |
148 | * 1.1.0 (Nicholas C. Zakas)
149 | * Fix release task (Nicholas C. Zakas)
150 | * Add changelog generation (Nicholas C. Zakas)
151 | * Update README for open sourcing (Nicholas C. Zakas)
152 | * Remove most jQuery references (Nicholas C. Zakas)
153 | * Switch to ShellJS and create dist files (Nicholas C. Zakas)
154 | * Add focusin and focusout events (Jeff Tan)
155 |
156 | 1.0.2 - February 20, 2015
157 |
158 | * v1.0.2 (Jeff Tan)
159 | * Detect circular service dependencies (fixes #51) (Nicholas C. Zakas)
160 |
--------------------------------------------------------------------------------
/examples/todo/js/behaviors/todo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview The TODO item behavior
3 | * @author Box
4 | */
5 |
6 | /*global Box*/
7 |
8 | /*
9 | * Handles behavior of todo items
10 | */
11 | Application.addBehavior('todo', function(context) {
12 |
13 | 'use strict';
14 |
15 | //--------------------------------------------------------------------------
16 | // Private
17 | //--------------------------------------------------------------------------
18 |
19 | var ENTER_KEY = 13,
20 | ESCAPE_KEY = 27;
21 |
22 | var todosDB,
23 | moduleEl;
24 |
25 | /**
26 | * Returns the nearest todo element
27 | * @param {HTMLElement} element A root/child node of a todo element to search from
28 | * @returns {HTMLElement}
29 | * @private
30 | */
31 | function getClosestTodoElement(element) {
32 |
33 | var matchesSelector = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.msMatchesSelector;
34 |
35 | while (element) {
36 | if (matchesSelector.bind(element)('li')) {
37 | return element;
38 | } else {
39 | element = element.parentNode;
40 | }
41 | }
42 | return false;
43 |
44 | }
45 |
46 | /**
47 | * Returns the id of the nearest todo element
48 | * @param {HTMLElement} element A root/child node of a todo element
49 | * @returns {string}
50 | * @private
51 | */
52 | function getClosestTodoId(element) {
53 | var todoEl = getClosestTodoElement(element);
54 | return todoEl.getAttribute('data-todo-id');
55 | }
56 |
57 | //--------------------------------------------------------------------------
58 | // Public
59 | //--------------------------------------------------------------------------
60 |
61 | return {
62 |
63 | /**
64 | * Initializes the module. Caches a data store object to todos
65 | * @returns {void}
66 | */
67 | init: function() {
68 | todosDB = context.getService('todos-db');
69 | moduleEl = context.getElement();
70 | },
71 |
72 | /**
73 | * Destroys the module.
74 | * @returns {void}
75 | */
76 | destroy: function() {
77 | todosDB = null;
78 | moduleEl = null;
79 | },
80 |
81 | /**
82 | * Handles all click events for the behavior.
83 | * @param {Event} event A DOM-normalized event object.
84 | * @param {HTMLElement} element The nearest HTML element with a data-type
85 | * attribute specified or null if there is none.
86 | * @param {string} elementType The value of data-type for the nearest
87 | * element with that attribute specified or null if there is none.
88 | * @returns {void}
89 | */
90 | onclick: function(event, element, elementType) {
91 |
92 | if (elementType === 'delete-btn') {
93 | var todoEl = getClosestTodoElement(element),
94 | todoId = getClosestTodoId(element);
95 |
96 | moduleEl.querySelector('#todo-list').removeChild(todoEl);
97 | todosDB.remove(todoId);
98 |
99 | context.broadcast('todoremoved', {
100 | id: todoId
101 | });
102 |
103 | }
104 |
105 | },
106 |
107 | /**
108 | * Handles change events for the behavior.
109 | * @param {Event} event A DOM-normalized event object.
110 | * @param {HTMLElement} element The nearest HTML element with a data-type
111 | * attribute specified or null if there is none.
112 | * @param {string} elementType The value of data-type for the nearest
113 | * element with that attribute specified or null if there is none.
114 | * @returns {void}
115 | */
116 | onchange: function(event, element, elementType) {
117 |
118 | if (elementType === 'mark-as-complete-checkbox') {
119 | var todoEl = getClosestTodoElement(element),
120 | todoId = getClosestTodoId(element);
121 |
122 | if (element.checked) {
123 | todoEl.classList.add('completed');
124 | todosDB.markAsComplete(todoId);
125 | } else {
126 | todoEl.classList.remove('completed');
127 | todosDB.markAsIncomplete(todoId);
128 | }
129 |
130 | context.broadcast('todostatuschange');
131 |
132 | }
133 |
134 | },
135 |
136 | /**
137 | * Handles double click events for the behavior.
138 | * @param {Event} event A DOM-normalized event object.
139 | * @param {HTMLElement} element The nearest HTML element with a data-type
140 | * attribute specified or null if there is none.
141 | * @param {string} elementType The value of data-type for the nearest
142 | * element with that attribute specified or null if there is none.
143 | * @returns {void}
144 | */
145 | ondblclick: function(event, element, elementType) {
146 |
147 | if (elementType === 'todo-label') {
148 | var todoEl = getClosestTodoElement(element);
149 |
150 | event.preventDefault();
151 | event.stopPropagation();
152 |
153 | this.showEditor(todoEl);
154 | }
155 |
156 | },
157 |
158 | /**
159 | * Handles keydown events for the behavior.
160 | * @param {Event} event A DOM-normalized event object.
161 | * @param {HTMLElement} element The nearest HTML element with a data-type
162 | * attribute specified or null if there is none.
163 | * @param {string} elementType The value of data-type for the nearest
164 | * element with that attribute specified or null if there is none.
165 | * @returns {void}
166 | */
167 | onkeydown: function(event, element, elementType) {
168 |
169 | if (elementType === 'edit-input') {
170 | var todoEl = getClosestTodoElement(element);
171 |
172 | if (event.keyCode === ENTER_KEY) {
173 | this.saveLabel(todoEl);
174 | this.hideEditor(todoEl);
175 | } else if (event.keyCode === ESCAPE_KEY) {
176 | this.hideEditor(todoEl);
177 | }
178 |
179 | }
180 |
181 | },
182 |
183 | /**
184 | * Displays a input box for the user to edit the label with
185 | * @param {HTMLElement} todoEl The todo element to edit
186 | * @returns {void}
187 | */
188 | showEditor: function(todoEl) {
189 | var todoId = getClosestTodoId(todoEl),
190 | editInputEl = todoEl.querySelector('.edit'),
191 | title = todosDB.get(todoId).title; // Grab current label
192 |
193 | // Set the edit input value to current label
194 | editInputEl.value = title;
195 |
196 | todoEl.classList.add('editing');
197 |
198 | // Place user cursor in the input
199 | editInputEl.focus();
200 | },
201 |
202 | /**
203 | * Hides the edit input for a given todo
204 | * @param {HTMLElement} todoEl The todo element
205 | * @returns {void}
206 | */
207 | hideEditor: function(todoEl) {
208 | todoEl.classList.remove('editing');
209 | },
210 |
211 | /**
212 | * Saves the value of the edit input and saves it to the db
213 | * @param {HTMLElement} todoEl The todo element to edit
214 | * @returns {void}
215 | */
216 | saveLabel: function(todoEl) {
217 | var todoId = getClosestTodoId(todoEl),
218 | editInputEl = todoEl.querySelector('.edit'),
219 | newTitle = (editInputEl.value).trim();
220 |
221 | todoEl.querySelector('label').textContent = newTitle;
222 | todosDB.edit(todoId, newTitle);
223 | }
224 | };
225 |
226 | });
227 |
--------------------------------------------------------------------------------
/dist/t3.min.js:
--------------------------------------------------------------------------------
1 | /*! t3 v2.7.0 */
2 | /*!
3 | Copyright 2016 Box, Inc. All rights reserved.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 | !function(e){var t={};if(t.EventTarget=function(){"use strict";function e(){this._handlers={}}return e.prototype={constructor:e,on:function(e,t){var n,r,i=this._handlers[e];for("undefined"==typeof i&&(i=this._handlers[e]=[]),n=0,r=i.length;r>n;n++)if(i[n]===t)return;i.push(t)},fire:function(e,t){var n,r,i,o={type:e,data:t};if(n=this._handlers[o.type],n instanceof Array)for(n=n.concat(),r=0,i=n.length;i>r;r++)n[r].call(this,o)},off:function(e,t){var n,r,i=this._handlers[e];if(i instanceof Array)for(n=0,r=i.length;r>n;n++)if(i[n]===t){i.splice(n,1);break}}},e}(),t.NativeDOM=function(){"use strict";return{type:"native",query:function(e,t){return e.querySelector(t)},queryAll:function(e,t){return e.querySelectorAll(t)},on:function(e,t,n){e.addEventListener(t,n,!1)},off:function(e,t,n){e.removeEventListener(t,n,!1)}}}(),t.DOM=t.NativeDOM,t.DOMEventDelegate=function(){"use strict";function e(e){return e&&e.hasAttribute("data-module")}function n(e){return e&&e.hasAttribute("data-type")}function r(t){for(var r=!1,i=!1;!r&&t&&t.parentNode&&!i;)r=n(t),i=e(t),r||(t=t.parentNode);return r?t:null}function i(e,t,n,r){var i,o;for(i=0;in;n++)if(e[n]===t)return n;return-1}function i(){y={},b={},E={},w=[],_={},D={},A=!1}function o(e){for(var t=0,n=w.length;n>t;t++)if(w[t]===e)return!0;return!1}function a(e){if("function"==typeof M)return void M(e);if(y.debug)throw e;O.fire("error",{exception:e})}function u(e,t){var n,r;for(n in e)r=e[n],"function"==typeof r&&(e[n]=function(e,n){return function(){var r=t+"."+e+"() - ";try{return n.apply(this,arguments)}catch(i){i.methodName=e,i.objectName=t,i.name=r+i.name,i.message=r+i.message,a(i)}}}(n,r))}function s(e){var t=e.getAttribute("data-module");return t?t.split(" ")[0]:""}function c(e,t){"function"==typeof e[t]&&e[t].apply(e,Array.prototype.slice.call(arguments,2))}function f(e){var t=E[e];if(t){if(!t.instance){if(o(e))return a(new ReferenceError("Circular service dependency: "+w.join(" -> ")+" -> "+e)),null;w.push(e),t.instance=t.creator(O),w.pop()}return t.instance}return a(new Error('Service "'+e+'" not found')),null}function l(e){var t,n,r,i,o,u=[],s={};for(n=e.instance.behaviors||[],t=0;tv;v++)h=p[v],c(h,"init");c(n.instance,"init"),d(n)}return this},stop:function(e){var t=v(e);if(t){p(t);for(var n,r=l(t),i=r.length-1;i>=0;i--)n=r[i],c(n,"destroy");c(t.instance,"destroy"),delete D[e.id]}else if(y.debug)return a(new Error("Unable to stop module associated with element: "+e.id)),this;return this},startAll:function(e){for(var n=t.DOM.queryAll(e,m),r=0,i=n.length;i>r;r++)this.start(n[r]);return this},stopAll:function(e){for(var n=t.DOM.queryAll(e,m),r=0,i=n.length;i>r;r++)this.stop(n[r]);return this},addModule:function(e,t){return"undefined"!=typeof b[e]?(a(new Error("Module "+e+" has already been added.")),this):(b[e]={creator:t,counter:1},this)},getModuleConfig:function(e,n){var r=v(e),i=null;if(r&&r.config)i=r.config;else{var o=t.DOM.query(e,'script[type="text/x-config"]');if(o)try{i=JSON.parse(o.text)}catch(u){a(new Error("Module with id "+e.id+" has a malformed config."))}r&&(r.config=i)}return i?"undefined"==typeof n?i:n in i?i[n]:null:null},addService:function(e,t){return"undefined"!=typeof E[e]?(a(new Error("Service "+e+" has already been added.")),this):(E[e]={creator:t,instance:null},this)},getService:f,hasService:function(e){return E.hasOwnProperty(e)},addBehavior:function(e,t){return"undefined"!=typeof _[e]?(a(new Error("Behavior "+e+" has already been added.")),this):(_[e]={creator:t,instance:null},this)},broadcast:function(e,t){var n,r,i,o,a;for(r in D)if(D.hasOwnProperty(r))for(i=D[r],g(i.instance,e,t),a=l(i),n=0;nn;n++)if(i[n]===t)return;i.push(t)},fire:function(e,t){var n,r,i,o={type:e,data:t};if(n=this._handlers[o.type],n instanceof Array)for(n=n.concat(),r=0,i=n.length;i>r;r++)n[r].call(this,o)},off:function(e,t){var n,r,i=this._handlers[e];if(i instanceof Array)for(n=0,r=i.length;r>n;n++)if(i[n]===t){i.splice(n,1);break}}},e}(),t.NativeDOM=function(){"use strict";return{type:"native",query:function(e,t){return e.querySelector(t)},queryAll:function(e,t){return e.querySelectorAll(t)},on:function(e,t,n){e.addEventListener(t,n,!1)},off:function(e,t,n){e.removeEventListener(t,n,!1)}}}(),t.DOM=t.NativeDOM,t.DOMEventDelegate=function(){"use strict";function e(e){return e&&e.hasAttribute("data-module")}function n(e){return e&&e.hasAttribute("data-type")}function r(t){for(var r=!1,i=!1;!r&&t&&t.parentNode&&!i;)r=n(t),i=e(t),r||(t=t.parentNode);return r?t:null}function i(e,t,n,r){var i,o;for(i=0;in;n++)if(e[n]===t)return n;return-1}function i(){y={},b={},E={},w=[],_={},D={},A=!1}function o(e){for(var t=0,n=w.length;n>t;t++)if(w[t]===e)return!0;return!1}function a(e){if("function"==typeof M)return void M(e);if(y.debug)throw e;O.fire("error",{exception:e})}function u(e,t){var n,r;for(n in e)r=e[n],"function"==typeof r&&(e[n]=function(e,n){return function(){var r=t+"."+e+"() - ";try{return n.apply(this,arguments)}catch(i){i.methodName=e,i.objectName=t,i.name=r+i.name,i.message=r+i.message,a(i)}}}(n,r))}function s(e){var t=e.getAttribute("data-module");return t?t.split(" ")[0]:""}function c(e,t){"function"==typeof e[t]&&e[t].apply(e,Array.prototype.slice.call(arguments,2))}function f(e){var t=E[e];if(t){if(!t.instance){if(o(e))return a(new ReferenceError("Circular service dependency: "+w.join(" -> ")+" -> "+e)),null;w.push(e),t.instance=t.creator(O),w.pop()}return t.instance}return a(new Error('Service "'+e+'" not found')),null}function l(e){var t,n,r,i,o,u=[],s={};for(n=e.instance.behaviors||[],t=0;tv;v++)h=p[v],c(h,"init");c(n.instance,"init"),d(n)}return this},stop:function(e){var t=v(e);if(t){p(t);for(var n,r=l(t),i=r.length-1;i>=0;i--)n=r[i],c(n,"destroy");c(t.instance,"destroy"),delete D[e.id]}else if(y.debug)return a(new Error("Unable to stop module associated with element: "+e.id)),this;return this},startAll:function(e){for(var n=t.DOM.queryAll(e,m),r=0,i=n.length;i>r;r++)this.start(n[r]);return this},stopAll:function(e){for(var n=t.DOM.queryAll(e,m),r=0,i=n.length;i>r;r++)this.stop(n[r]);return this},addModule:function(e,t){return"undefined"!=typeof b[e]?(a(new Error("Module "+e+" has already been added.")),this):(b[e]={creator:t,counter:1},this)},getModuleConfig:function(e,n){var r=v(e),i=null;if(r&&r.config)i=r.config;else{var o=t.DOM.query(e,'script[type="text/x-config"]');if(o)try{i=JSON.parse(o.text)}catch(u){a(new Error("Module with id "+e.id+" has a malformed config."))}r&&(r.config=i)}return i?"undefined"==typeof n?i:n in i?i[n]:null:null},addService:function(e,t){return"undefined"!=typeof E[e]?(a(new Error("Service "+e+" has already been added.")),this):(E[e]={creator:t,instance:null},this)},getService:f,hasService:function(e){return E.hasOwnProperty(e)},addBehavior:function(e,t){return"undefined"!=typeof _[e]?(a(new Error("Behavior "+e+" has already been added.")),this):(_[e]={creator:t,instance:null},this)},broadcast:function(e,t){var n,r,i,o,a;for(r in D)if(D.hasOwnProperty(r))for(i=D[r],g(i.instance,e,t),a=l(i),n=0;nn;n++)if(i[n]===t)return;i.push(t)},fire:function(e,t){var n,r,i,o={type:e,data:t};if(n=this._handlers[o.type],n instanceof Array)for(n=n.concat(),r=0,i=n.length;i>r;r++)n[r].call(this,o)},off:function(e,t){var n,r,i=this._handlers[e];if(i instanceof Array)for(n=0,r=i.length;r>n;n++)if(i[n]===t){i.splice(n,1);break}}},e}(),t.JQueryDOM=function(){"use strict";return{type:"jquery",query:function(e,t){return jQuery(e).find(t)[0]||null},queryAll:function(e,t){return jQuery.makeArray(jQuery(e).find(t))},on:function(e,t,n){jQuery(e).on(t,n)},off:function(e,t,n){jQuery(e).off(t,n)}}}(),t.DOM=t.JQueryDOM,t.DOMEventDelegate=function(){"use strict";function e(e){return e&&e.hasAttribute("data-module")}function n(e){return e&&e.hasAttribute("data-type")}function r(t){for(var r=!1,i=!1;!r&&t&&t.parentNode&&!i;)r=n(t),i=e(t),r||(t=t.parentNode);return r?t:null}function i(e,t,n,r){var i,o;for(i=0;in;n++)if(e[n]===t)return n;return-1}function i(){m={},b={},E={},w=[],_={},D={},A=!1}function o(e){for(var t=0,n=w.length;n>t;t++)if(w[t]===e)return!0;return!1}function a(e){if("function"==typeof M)return void M(e);if(m.debug)throw e;O.fire("error",{exception:e})}function u(e,t){var n,r;for(n in e)r=e[n],"function"==typeof r&&(e[n]=function(e,n){return function(){var r=t+"."+e+"() - ";try{return n.apply(this,arguments)}catch(i){i.methodName=e,i.objectName=t,i.name=r+i.name,i.message=r+i.message,a(i)}}}(n,r))}function s(e){var t=e.getAttribute("data-module");return t?t.split(" ")[0]:""}function c(e,t){"function"==typeof e[t]&&e[t].apply(e,Array.prototype.slice.call(arguments,2))}function f(e){var t=E[e];if(t){if(!t.instance){if(o(e))return a(new ReferenceError("Circular service dependency: "+w.join(" -> ")+" -> "+e)),null;w.push(e),t.instance=t.creator(O),w.pop()}return t.instance}return a(new Error('Service "'+e+'" not found')),null}function l(e){var t,n,r,i,o,u=[],s={};for(n=e.instance.behaviors||[],t=0;tg;g++)h=p[g],c(h,"init");c(n.instance,"init"),d(n)}return this},stop:function(e){var t=g(e);if(t){p(t);for(var n,r=l(t),i=r.length-1;i>=0;i--)n=r[i],c(n,"destroy");c(t.instance,"destroy"),delete D[e.id]}else if(m.debug)return a(new Error("Unable to stop module associated with element: "+e.id)),this;return this},startAll:function(e){for(var n=t.DOM.queryAll(e,y),r=0,i=n.length;i>r;r++)this.start(n[r]);return this},stopAll:function(e){for(var n=t.DOM.queryAll(e,y),r=0,i=n.length;i>r;r++)this.stop(n[r]);return this},addModule:function(e,t){return"undefined"!=typeof b[e]?(a(new Error("Module "+e+" has already been added.")),this):(b[e]={creator:t,counter:1},this)},getModuleConfig:function(e,n){var r=g(e),i=null;if(r&&r.config)i=r.config;else{var o=t.DOM.query(e,'script[type="text/x-config"]');if(o)try{i=JSON.parse(o.text)}catch(u){a(new Error("Module with id "+e.id+" has a malformed config."))}r&&(r.config=i)}return i?"undefined"==typeof n?i:n in i?i[n]:null:null},addService:function(e,t){return"undefined"!=typeof E[e]?(a(new Error("Service "+e+" has already been added.")),this):(E[e]={creator:t,instance:null},this)},getService:f,hasService:function(e){return E.hasOwnProperty(e)},addBehavior:function(e,t){return"undefined"!=typeof _[e]?(a(new Error("Behavior "+e+" has already been added.")),this):(_[e]={creator:t,instance:null},this)},broadcast:function(e,t){var n,r,i,o,a;for(r in D)if(D.hasOwnProperty(r))for(i=D[r],v(i.instance,e,t),a=l(i),n=0;nn;n++)if(i[n]===t)return;i.push(t)},fire:function(e,t){var n,r,i,o={type:e,data:t};if(n=this._handlers[o.type],n instanceof Array)for(n=n.concat(),r=0,i=n.length;i>r;r++)n[r].call(this,o)},off:function(e,t){var n,r,i=this._handlers[e];if(i instanceof Array)for(n=0,r=i.length;r>n;n++)if(i[n]===t){i.splice(n,1);break}}},e}(),t.NativeDOM=function(){"use strict";return{type:"native",query:function(e,t){return e.querySelector(t)},queryAll:function(e,t){return e.querySelectorAll(t)},on:function(e,t,n){e.addEventListener(t,n,!1)},off:function(e,t,n){e.removeEventListener(t,n,!1)}}}(),t.DOM=t.NativeDOM,t.DOMEventDelegate=function(){"use strict";function e(e){return e&&e.hasAttribute("data-module")}function n(e){return e&&e.hasAttribute("data-type")}function r(t){for(var r=!1,i=!1;!r&&t&&t.parentNode&&!i;)r=n(t),i=e(t),r||(t=t.parentNode);return r?t:null}function i(e,t,n,r){var i,o;for(i=0;in;n++)if(e[n]===t)return n;return-1}function i(){y={},b={},E={},w=[],_={},D={},A=!1}function o(e){for(var t=0,n=w.length;n>t;t++)if(w[t]===e)return!0;return!1}function a(e){if("function"==typeof M)return void M(e);if(y.debug)throw e;O.fire("error",{exception:e})}function u(e,t){var n,r;for(n in e)r=e[n],"function"==typeof r&&(e[n]=function(e,n){return function(){var r=t+"."+e+"() - ";try{return n.apply(this,arguments)}catch(i){i.methodName=e,i.objectName=t,i.name=r+i.name,i.message=r+i.message,a(i)}}}(n,r))}function s(e){var t=e.getAttribute("data-module");return t?t.split(" ")[0]:""}function c(e,t){"function"==typeof e[t]&&e[t].apply(e,Array.prototype.slice.call(arguments,2))}function f(e){var t=E[e];if(t){if(!t.instance){if(o(e))return a(new ReferenceError("Circular service dependency: "+w.join(" -> ")+" -> "+e)),null;w.push(e),t.instance=t.creator(O),w.pop()}return t.instance}return a(new Error('Service "'+e+'" not found')),null}function l(e){var t,n,r,i,o,u=[],s={};for(n=e.instance.behaviors||[],t=0;tv;v++)h=p[v],c(h,"init");c(n.instance,"init"),d(n)}return this},stop:function(e){var t=v(e);if(t){p(t);for(var n,r=l(t),i=r.length-1;i>=0;i--)n=r[i],c(n,"destroy");c(t.instance,"destroy"),delete D[e.id]}else if(y.debug)return a(new Error("Unable to stop module associated with element: "+e.id)),this;return this},startAll:function(e){for(var n=t.DOM.queryAll(e,m),r=0,i=n.length;i>r;r++)this.start(n[r]);return this},stopAll:function(e){for(var n=t.DOM.queryAll(e,m),r=0,i=n.length;i>r;r++)this.stop(n[r]);return this},addModule:function(e,t){return"undefined"!=typeof b[e]?(a(new Error("Module "+e+" has already been added.")),this):(b[e]={creator:t,counter:1},this)},getModuleConfig:function(e,n){var r=v(e),i=null;if(r&&r.config)i=r.config;else{var o=t.DOM.query(e,'script[type="text/x-config"]');if(o)try{i=JSON.parse(o.text)}catch(u){a(new Error("Module with id "+e.id+" has a malformed config."))}r&&(r.config=i)}return i?"undefined"==typeof n?i:n in i?i[n]:null:null},addService:function(e,t){return"undefined"!=typeof E[e]?(a(new Error("Service "+e+" has already been added.")),this):(E[e]={creator:t,instance:null},this)},getService:f,hasService:function(e){return E.hasOwnProperty(e)},addBehavior:function(e,t){return"undefined"!=typeof _[e]?(a(new Error("Behavior "+e+" has already been added.")),this):(_[e]={creator:t,instance:null},this)},broadcast:function(e,t){var n,r,i,o,a;for(r in D)if(D.hasOwnProperty(r))for(i=D[r],g(i.instance,e,t),a=l(i),n=0;nn;n++)if(i[n]===t)return;i.push(t)},fire:function(e,t){var n,r,i,o={type:e,data:t};if(n=this._handlers[o.type],n instanceof Array)for(n=n.concat(),r=0,i=n.length;i>r;r++)n[r].call(this,o)},off:function(e,t){var n,r,i=this._handlers[e];if(i instanceof Array)for(n=0,r=i.length;r>n;n++)if(i[n]===t){i.splice(n,1);break}}},e}(),t.JQueryDOM=function(){"use strict";return{type:"jquery",query:function(e,t){return jQuery(e).find(t)[0]||null},queryAll:function(e,t){return jQuery.makeArray(jQuery(e).find(t))},on:function(e,t,n){jQuery(e).on(t,n)},off:function(e,t,n){jQuery(e).off(t,n)}}}(),t.DOM=t.JQueryDOM,t.DOMEventDelegate=function(){"use strict";function e(e){return e&&e.hasAttribute("data-module")}function n(e){return e&&e.hasAttribute("data-type")}function r(t){for(var r=!1,i=!1;!r&&t&&t.parentNode&&!i;)r=n(t),i=e(t),r||(t=t.parentNode);return r?t:null}function i(e,t,n,r){var i,o;for(i=0;in;n++)if(e[n]===t)return n;return-1}function i(){m={},b={},E={},w=[],_={},D={},A=!1}function o(e){for(var t=0,n=w.length;n>t;t++)if(w[t]===e)return!0;return!1}function a(e){if("function"==typeof M)return void M(e);if(m.debug)throw e;O.fire("error",{exception:e})}function u(e,t){var n,r;for(n in e)r=e[n],"function"==typeof r&&(e[n]=function(e,n){return function(){var r=t+"."+e+"() - ";try{return n.apply(this,arguments)}catch(i){i.methodName=e,i.objectName=t,i.name=r+i.name,i.message=r+i.message,a(i)}}}(n,r))}function s(e){var t=e.getAttribute("data-module");return t?t.split(" ")[0]:""}function c(e,t){"function"==typeof e[t]&&e[t].apply(e,Array.prototype.slice.call(arguments,2))}function f(e){var t=E[e];if(t){if(!t.instance){if(o(e))return a(new ReferenceError("Circular service dependency: "+w.join(" -> ")+" -> "+e)),null;w.push(e),t.instance=t.creator(O),w.pop()}return t.instance}return a(new Error('Service "'+e+'" not found')),null}function l(e){var t,n,r,i,o,u=[],s={};for(n=e.instance.behaviors||[],t=0;tg;g++)h=p[g],c(h,"init");c(n.instance,"init"),d(n)}return this},stop:function(e){var t=g(e);if(t){p(t);for(var n,r=l(t),i=r.length-1;i>=0;i--)n=r[i],c(n,"destroy");c(t.instance,"destroy"),delete D[e.id]}else if(m.debug)return a(new Error("Unable to stop module associated with element: "+e.id)),this;return this},startAll:function(e){for(var n=t.DOM.queryAll(e,y),r=0,i=n.length;i>r;r++)this.start(n[r]);return this},stopAll:function(e){for(var n=t.DOM.queryAll(e,y),r=0,i=n.length;i>r;r++)this.stop(n[r]);return this},addModule:function(e,t){return"undefined"!=typeof b[e]?(a(new Error("Module "+e+" has already been added.")),this):(b[e]={creator:t,counter:1},this)},getModuleConfig:function(e,n){var r=g(e),i=null;if(r&&r.config)i=r.config;else{var o=t.DOM.query(e,'script[type="text/x-config"]');if(o)try{i=JSON.parse(o.text)}catch(u){a(new Error("Module with id "+e.id+" has a malformed config."))}r&&(r.config=i)}return i?"undefined"==typeof n?i:n in i?i[n]:null:null},addService:function(e,t){return"undefined"!=typeof E[e]?(a(new Error("Service "+e+" has already been added.")),this):(E[e]={creator:t,instance:null},this)},getService:f,hasService:function(e){return E.hasOwnProperty(e)},addBehavior:function(e,t){return"undefined"!=typeof _[e]?(a(new Error("Behavior "+e+" has already been added.")),this):(_[e]={creator:t,instance:null},this)},broadcast:function(e,t){var n,r,i,o,a;for(r in D)if(D.hasOwnProperty(r))for(i=D[r],v(i.instance,e,t),a=l(i),n=0;n
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | ```
61 |
62 | You may also use `bower` to install t3js:
63 |
64 | ```
65 | bower install t3js
66 | ```
67 |
68 | ## Upgrade from 1.5.1 to 2.0.0
69 |
70 | T3 2.0.0 was released on November 18th, 2015 with some major changes. To upgrade, please see [these instructions](http://t3js.org/docs/getting-started/migrating-to-2-0-0).
71 |
72 | ## Getting Started
73 |
74 | Your T3 front-end is made up of modules, so the first step is to indicate which modules are responsible for which parts of the page. You can do that by using the `data-module` attribute and specifying the module ID, such as:
75 |
76 | ```html
77 |
78 |
Box
79 |
80 |
81 | ```
82 |
83 | This example specifies the module `header` should manage this particular part of the page. The module `header` is then defined as:
84 |
85 | ```js
86 | Box.Application.addModule('header', function(context) {
87 |
88 | return {
89 |
90 | onclick: function(event, element, elementType) {
91 | if (elementType === 'welcome-btn') {
92 | alert('Welcome, T3 user!');
93 | } else {
94 | alert('You clicked outside the button.');
95 | }
96 | }
97 |
98 | };
99 |
100 | });
101 | ```
102 |
103 | This is a very simple module that has an `onclick` handler. T3 automatically wires up specified event handlers so you don't have to worry about using event delegation or removing event handlers when they are no longer needed. The `onclick` handler receives a DOM-normalized event object that can be used to get event details. When the button is clicked, a message is displayed. Additionally, clicking anywhere inside the module will display a different message. Event handlers are tied to the entire module area, so there's no need to attach multiple handlers of the same type.
104 |
105 | The last step is to initialize the T3 application:
106 |
107 | ```js
108 | Box.Application.init();
109 | ```
110 |
111 | This call starts all modules on the page (be sure to include both the T3 library and your module code before calling `init()`). We recommend calling `init()` as soon as possible after your JavaScript is loaded. Whether you do that `onload`, earlier, or later, is completely up to you.
112 |
113 | There are more extensive tutorials and examples on our [website](http://t3js.org).
114 |
115 | ## Browser Support
116 |
117 | T3 is tested and known to work in the following browsers:
118 |
119 | * Internet Explorer 9 and higher
120 | * Firefox (latest version)
121 | * Chrome (latest version)
122 | * Safari (latest version)
123 |
124 | With the exception of Internet Explorer, T3 will continue to support the current and previous one version of all major browsers.
125 |
126 | ## Contributing
127 |
128 | The main purpose of sharing T3 is to continue its development, making it faster, more efficient, and easier to use.
129 |
130 | ### Directory Structure
131 |
132 | * `config` - configuration files for the project
133 | * `dist` - browser bundles (this directory is updated automatically with each release)
134 | * `lib` - the source code as individual files
135 | * `tests` - the test code
136 |
137 | ### Prerequisites
138 |
139 | In order to get started contributing to T3, you'll need to be familiar and have installed:
140 |
141 | 1. [Git](http://git-scm.com/)
142 | 1. [npm](https://npmjs.org)
143 | 1. [Node.js](https://nodejs.org) or [IO.js](https://iojs.org)
144 |
145 | ### Setup
146 |
147 | Following the instructions in the [contributor guidelines](CONTRIBUTING.md) to setup a local copy of the T3 repository.
148 |
149 | Once you clone the T3 git repository, run the following inside the `t3js` directory:
150 |
151 | ```
152 | $ npm i
153 | ```
154 |
155 | This sets up all the dependencies that the T3 build system needs to function.
156 |
157 | **Note:** You'll need to do this periodically when pulling the latest code from our repository as dependencies might change. If you find unexpected errors, be sure to run `npm i` again to ensure your dependencies are up-to-date.
158 |
159 | ### Running Tests
160 |
161 | After that, you can run all tests by running:
162 |
163 | ```
164 | $ npm test
165 | ```
166 |
167 | This will start by linting the code and then running all unit tests.
168 |
169 | ### Build Commands
170 |
171 | The following build commands are available:
172 |
173 | 1. `npm test` - runs all linting and uni tests
174 | 1. `npm run lint` - runs all linting
175 | 1. `npm run dist` - creates the browser bundles and places them in `/dist`
176 |
177 | ## Frequently Asked Questions
178 |
179 | ### Can I use this with older browsers?
180 |
181 | We provide a custom version of T3 built with jQuery that is compatible with older browsers such as IE8.
182 |
183 | ### Why support IE8?
184 |
185 | The Box web application currently supports IE8 with a [planned end-of-life of December 31, 2015](https://support.box.com/hc/en-us/articles/200519838-What-Is-the-Box-Policy-for-Browser-and-OS-Support-). We will be dropping T3 support for IE8 at the same time.
186 |
187 | ## Support
188 |
189 | Need to contact us directly? Email [t3js@box.com](mailto:t3js@box.com) with your questions or comments.
190 |
191 | ## Copyright and License
192 |
193 | Copyright 2015 Box, Inc. All rights reserved.
194 |
195 | Licensed under the Apache License, Version 2.0 (the "License");
196 | you may not use this file except in compliance with the License.
197 | You may obtain a copy of the License at
198 |
199 | http://www.apache.org/licenses/LICENSE-2.0
200 |
201 | Unless required by applicable law or agreed to in writing, software
202 | distributed under the License is distributed on an "AS IS" BASIS,
203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
204 | See the License for the specific language governing permissions and
205 | limitations under the License.
206 |
207 | [npm-image]: https://img.shields.io/npm/v/t3js.svg?style=flat-square
208 | [npm-url]: https://npmjs.org/package/t3js
209 | [travis-image]: https://img.shields.io/travis/box/t3js/master.svg?style=flat-square
210 | [travis-url]: https://travis-ci.org/box/t3js
211 |
--------------------------------------------------------------------------------
/dist/t3.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"t3.min.js.map","sources":["./dist/t3.js"],"names":["window","Box","EventTarget","this","_handlers","prototype","constructor","on","type","handler","i","len","handlers","length","push","fire","data","event","Array","concat","call","off","splice","NativeDOM","query","root","selector","querySelector","queryAll","querySelectorAll","element","listener","addEventListener","removeEventListener","DOM","DOMEventDelegate","isModuleElement","hasAttribute","isTypeElement","getNearestTypeElement","found","moduleBoundaryReached","parentNode","forEachEventType","eventTypes","callback","thisValue","_handler","_eventTypes","DEFAULT_EVENT_TYPES","_boundHandler","_attached","_handleEvent","targetElement","target","elementType","getAttribute","attachEvents","eventType","handleEvent","that","apply","arguments","detachEvents","Context","application","broadcast","name","getService","serviceName","hasService","getConfig","getModuleConfig","getGlobal","getGlobalConfig","reportError","exception","getElement","Application","assign","receiver","supplier","prop","hasOwnProperty","indexOf","items","item","reset","globalConfig","modules","services","serviceStack","behaviors","instances","initialized","isServiceBeingInstantiated","error","customErrorHandler","debug","captureObjectErrors","object","objectName","propertyName","propertyValue","methodName","method","errorPrefix","ex","message","getModuleName","moduleAttribute","split","callModuleMethod","instance","slice","serviceData","ReferenceError","join","creator","pop","Error","getBehaviors","instanceData","behaviorNames","behaviorData","moduleBehaviorInstances","behaviorName","behaviorInstances","includedBehaviors","context","createAndBindEventDelegate","eventDelegates","delegate","bindEventListeners","moduleBehaviors","unbindEventListeners","getInstanceDataByElement","id","callMessageHandler","onmessage","messages","MODULE_SELECTOR","init","params","startAll","document","documentElement","destroy","stopAll","isStarted","start","module","moduleName","moduleData","counter","behaviorInstance","stop","moduleElements","addModule","moduleConfig","config","configElement","JSON","parse","text","addService","addBehavior","messageData","setGlobalConfig","setErrorHandler","exceptionHandler","reportWarning","globalConsole","warn","reportInfo","info","define","amd","exports","key"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/t3-jquery.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"t3-jquery.min.js.map","sources":["./dist/t3-jquery.js"],"names":["window","Box","EventTarget","this","_handlers","prototype","constructor","on","type","handler","i","len","handlers","length","push","fire","data","event","Array","concat","call","off","splice","JQueryDOM","query","root","selector","jQuery","find","queryAll","makeArray","element","listener","DOM","DOMEventDelegate","isModuleElement","hasAttribute","isTypeElement","getNearestTypeElement","found","moduleBoundaryReached","parentNode","forEachEventType","eventTypes","callback","thisValue","_handler","_eventTypes","DEFAULT_EVENT_TYPES","_boundHandler","_attached","_handleEvent","targetElement","target","elementType","getAttribute","attachEvents","eventType","handleEvent","that","apply","arguments","detachEvents","Context","application","broadcast","name","getService","serviceName","hasService","getConfig","getModuleConfig","getGlobal","getGlobalConfig","reportError","exception","getElement","Application","assign","receiver","supplier","prop","hasOwnProperty","indexOf","items","item","reset","globalConfig","modules","services","serviceStack","behaviors","instances","initialized","isServiceBeingInstantiated","error","customErrorHandler","debug","captureObjectErrors","object","objectName","propertyName","propertyValue","methodName","method","errorPrefix","ex","message","getModuleName","moduleAttribute","split","callModuleMethod","instance","slice","serviceData","ReferenceError","join","creator","pop","Error","getBehaviors","instanceData","behaviorNames","behaviorData","moduleBehaviorInstances","behaviorName","behaviorInstances","includedBehaviors","context","createAndBindEventDelegate","eventDelegates","delegate","bindEventListeners","moduleBehaviors","unbindEventListeners","getInstanceDataByElement","id","callMessageHandler","onmessage","messages","MODULE_SELECTOR","init","params","startAll","document","documentElement","destroy","stopAll","isStarted","start","module","moduleName","moduleData","counter","behaviorInstance","stop","moduleElements","addModule","moduleConfig","config","configElement","JSON","parse","text","addService","addBehavior","messageData","setGlobalConfig","setErrorHandler","exceptionHandler","reportWarning","globalConsole","warn","reportInfo","info","define","amd","exports","key"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/t3-native.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"t3-native.min.js.map","sources":["./dist/t3-native.js"],"names":["window","Box","EventTarget","this","_handlers","prototype","constructor","on","type","handler","i","len","handlers","length","push","fire","data","event","Array","concat","call","off","splice","NativeDOM","query","root","selector","querySelector","queryAll","querySelectorAll","element","listener","addEventListener","removeEventListener","DOM","DOMEventDelegate","isModuleElement","hasAttribute","isTypeElement","getNearestTypeElement","found","moduleBoundaryReached","parentNode","forEachEventType","eventTypes","callback","thisValue","_handler","_eventTypes","DEFAULT_EVENT_TYPES","_boundHandler","_attached","_handleEvent","targetElement","target","elementType","getAttribute","attachEvents","eventType","handleEvent","that","apply","arguments","detachEvents","Context","application","broadcast","name","getService","serviceName","hasService","getConfig","getModuleConfig","getGlobal","getGlobalConfig","reportError","exception","getElement","Application","assign","receiver","supplier","prop","hasOwnProperty","indexOf","items","item","reset","globalConfig","modules","services","serviceStack","behaviors","instances","initialized","isServiceBeingInstantiated","error","customErrorHandler","debug","captureObjectErrors","object","objectName","propertyName","propertyValue","methodName","method","errorPrefix","ex","message","getModuleName","moduleAttribute","split","callModuleMethod","instance","slice","serviceData","ReferenceError","join","creator","pop","Error","getBehaviors","instanceData","behaviorNames","behaviorData","moduleBehaviorInstances","behaviorName","behaviorInstances","includedBehaviors","context","createAndBindEventDelegate","eventDelegates","delegate","bindEventListeners","moduleBehaviors","unbindEventListeners","getInstanceDataByElement","id","callMessageHandler","onmessage","messages","MODULE_SELECTOR","init","params","startAll","document","documentElement","destroy","stopAll","isStarted","start","module","moduleName","moduleData","counter","behaviorInstance","stop","moduleElements","addModule","moduleConfig","config","configElement","JSON","parse","text","addService","addBehavior","messageData","setGlobalConfig","setErrorHandler","exceptionHandler","reportWarning","globalConsole","warn","reportInfo","info","define","amd","exports","key"],"mappings":";;;;;;;;;;;;;;;;CAkBC,SAASA,GAaV,GAAIC,KA60CH,IAn0CDA,EAAIC,YAAe,WAElB,YAOA,SAASA,KAQRC,KAAKC,aAuFN,MApFAF,GAAYG,WAGXC,YAAaJ,EAQbK,GAAI,SAASC,EAAMC,GAElB,GACCC,GACAC,EAFGC,EAAWT,KAAKC,UAAUI,EAQ9B,KAJwB,mBAAbI,KACVA,EAAWT,KAAKC,UAAUI,OAGtBE,EAAI,EAAGC,EAAMC,EAASC,OAAYF,EAAJD,EAASA,IAC3C,GAAIE,EAASF,KAAOD,EAEnB,MAIFG,GAASE,KAAKL,IAUfM,KAAM,SAASP,EAAMQ,GAEpB,GAAIJ,GACHF,EACAC,EACAM,GACCT,KAAMA,EACNQ,KAAMA,EAKR,IADAJ,EAAWT,KAAKC,UAAUa,EAAMT,MAC5BI,YAAoBM,OAKvB,IADAN,EAAWA,EAASO,SACfT,EAAI,EAAGC,EAAMC,EAASC,OAAYF,EAAJD,EAASA,IAC3CE,EAASF,GAAGU,KAAKjB,KAAMc,IAW1BI,IAAK,SAASb,EAAMC,GAEnB,GACCC,GACAC,EAFGC,EAAWT,KAAKC,UAAUI,EAI9B,IAAII,YAAoBM,OACvB,IAAKR,EAAI,EAAGC,EAAMC,EAASC,OAAYF,EAAJD,EAASA,IAC3C,GAAIE,EAASF,KAAOD,EAAS,CAC5BG,EAASU,OAAOZ,EAAG,EACnB,UAOER,KAWRD,EAAIsB,UAAa,WAChB,YAEA,QAECf,KAAM,SAUNgB,MAAO,SAASC,EAAMC,GACrB,MAAOD,GAAKE,cAAcD,IAW3BE,SAAU,SAASH,EAAMC,GACxB,MAAOD,GAAKI,iBAAiBH,IAW9BnB,GAAI,SAASuB,EAAStB,EAAMuB,GAC3BD,EAAQE,iBAAiBxB,EAAMuB,GAAU,IAW1CV,IAAK,SAASS,EAAStB,EAAMuB,GAC5BD,EAAQG,oBAAoBzB,EAAMuB,GAAU,QAK/C9B,EAAIiC,IAAMjC,EAAIsB,UAQdtB,EAAIkC,iBAAoB,WAEvB,YAcA,SAASC,GAAgBN,GACxB,MAAOA,IAAWA,EAAQO,aAAa,eASxC,QAASC,GAAcR,GACtB,MAAOA,IAAWA,EAAQO,aAAa,aASxC,QAASE,GAAsBT,GAS9B,IARA,GAAIU,IAAQ,EACRC,GAAwB,GAOpBD,GAASV,GAAWA,EAAQY,aAAeD,GAClDD,EAAQF,EAAcR,GACtBW,EAAwBL,EAAgBN,GAEnCU,IACJV,EAAUA,EAAQY,WAKpB,OAAOF,GAAQV,EAAU,KAa1B,QAASa,GAAiBC,EAAYnC,EAASoC,EAAUC,GAExD,GAAIpC,GACHF,CAED,KAAKE,EAAI,EAAGA,EAAIkC,EAAW/B,OAAQH,IAClCF,EAAOoC,EAAWlC,GAGdD,EAAQ,KAAOD,IAClBqC,EAASzB,KAAK0B,EAAWtC,GAY5B,QAAS2B,GAAiBL,EAASrB,EAASmC,GAM3CzC,KAAK2B,QAAUA,EAOf3B,KAAK4C,SAAWtC,EAOhBN,KAAK6C,YAAcJ,GAAcK,EAQjC9C,KAAK+C,iBAOL/C,KAAKgD,WAAY,EAxHlB,GAAIF,IAAuB,QAAS,YAAa,WAAY,YAAa,UACxE,aAAc,aAAc,YAAa,UAAW,QAAS,SAAU,SACvE,cAAe,WAAY,QAAS,UAAW,WAwKjD,OA9CAd,GAAiB9B,WAGhBC,YAAa6B,EAEbiB,aAAc,SAASnC,GACtB,GAAIoC,GAAgBd,EAAsBtB,EAAMqC,QAC/CC,EAAcF,EAAgBA,EAAcG,aAAa,aAAe,EAEzErD,MAAK4C,SAAS,KAAO9B,EAAMT,MAAMS,EAAOoC,EAAeE,IAOxDE,aAAc,WACRtD,KAAKgD,YAETR,EAAiBxC,KAAK6C,YAAa7C,KAAK4C,SAAU,SAASW,GAG1D,QAASC,KACRC,EAAKR,aAAaS,MAAMD,EAAME,WAH/B,GAAIF,GAAOzD,IAMXF,GAAIiC,IAAI3B,GAAGJ,KAAK2B,QAAS4B,EAAWC,GAEpCxD,KAAK+C,cAAcQ,GAAaC,GAC9BxD,MAEHA,KAAKgD,WAAY,IAQnBY,aAAc,WACbpB,EAAiBxC,KAAK6C,YAAa7C,KAAK4C,SAAU,SAASW,GAC1DzD,EAAIiC,IAAIb,IAAIlB,KAAK2B,QAAS4B,EAAWvD,KAAK+C,cAAcQ,KACtDvD,QAIEgC,KAURlC,EAAI+D,QAAW,WAEd,YASA,SAASA,GAAQC,EAAanC,GAC7B3B,KAAK8D,YAAcA,EACnB9D,KAAK2B,QAAUA,EA4FhB,MArFAkC,GAAQ3D,WACPC,YAAa0D,EAQbE,UAAW,SAASC,EAAMnD,GACzBb,KAAK8D,YAAYC,UAAUC,EAAMnD,IAQlCoD,WAAY,SAASC,GACpB,MAAOlE,MAAK8D,YAAYG,WAAWC,IAQpCC,WAAY,SAASD,GACpB,MAAOlE,MAAK8D,YAAYK,WAAWD,IAUpCE,UAAW,SAASJ,GACnB,MAAOhE,MAAK8D,YAAYO,gBAAgBrE,KAAK2B,QAASqC,IAQvDM,UAAW,SAASN,GACnB,MAAOhE,MAAK8D,YAAYQ,UAAUN,IASnCO,gBAAiB,SAASP,GACzB,MAAOhE,MAAK8D,YAAYS,gBAAgBP,IASzCQ,YAAa,SAASC,GACrBzE,KAAK8D,YAAYU,YAAYC,IAW9BC,WAAY,WACX,MAAO1E,MAAK2B,UAKPkC,KAeR/D,EAAI6E,YAAe,WAElB,YAoDA,SAASC,GAAOC,EAAUC,GAEzB,IAAK,GAAIC,KAAQD,GACZA,EAASE,eAAeD,KAC3BF,EAASE,GAAQD,EAASC,GAI5B,OAAOF,GAUR,QAASI,GAAQC,EAAOC,GACvB,IAAK,GAAI5E,GAAI,EAAGC,EAAM0E,EAAMxE,OAAYF,EAAJD,EAASA,IAC5C,GAAI2E,EAAM3E,KAAO4E,EAChB,MAAO5E,EAIT,OAAO,GAQR,QAAS6E,KACRC,KACAC,KACAC,KACAC,KACAC,KACAC,KACAC,GAAc,EAef,QAASC,GAA2B1B,GACnC,IAAK,GAAI3D,GAAI,EAAGC,EAAMgF,EAAa9E,OAAYF,EAAJD,EAASA,IACnD,GAAIiF,EAAajF,KAAO2D,EACvB,OAAO,CAIT,QAAO,EAUR,QAAS2B,GAAMpB,GACd,GAAkC,kBAAvBqB,GAEV,WADAA,GAAmBrB,EAGpB,IAAIY,EAAaU,MAChB,KAAMtB,EAENX,GAAYlD,KAAK,SAChB6D,UAAWA,IAgBd,QAASuB,GAAoBC,EAAQC,GAEpC,GAAIC,GACHC,CAGD,KAAKD,IAAgBF,GACpBG,EAAgBH,EAAOE,GAGM,kBAAlBC,KAQVH,EAAOE,GAAiB,SAASE,EAAYC,GAC5C,MAAO,YACN,GAAIC,GAAcL,EAAa,IAAMG,EAAa,OAClD,KACC,MAAOC,GAAO5C,MAAM1D,KAAM2D,WACzB,MAAO6C,GACRA,EAAGH,WAAaA,EAChBG,EAAGN,WAAaA,EAChBM,EAAGxC,KAAOuC,EAAcC,EAAGxC,KAC3BwC,EAAGC,QAAUF,EAAcC,EAAGC,QAC9BZ,EAAMW,MAIPL,EAAcC,IAYnB,QAASM,GAAc/E,GACtB,GAAIgF,GAAkBhF,EAAQ0B,aAAa,cAE3C,OAAIsD,GACIA,EAAgBC,MAAM,KAAK,GAE5B,GAWR,QAASC,GAAiBC,EAAUR,GACH,kBAArBQ,GAASR,IAEnBQ,EAASR,GAAQ5C,MAAMoD,EAAU/F,MAAMb,UAAU6G,MAAM9F,KAAK0C,UAAW,IAWzE,QAASM,GAAWC,GAEnB,GAAI8C,GAAczB,EAASrB,EAE3B,IAAI8C,EAAa,CAEhB,IAAKA,EAAYF,SAAU,CAE1B,GAAIlB,EAA2B1B,GAE9B,MADA2B,GAAM,GAAIoB,gBAAe,gCAAkCzB,EAAa0B,KAAK,QAAU,OAAShD,IACzF,IAIRsB,GAAa7E,KAAKuD,GAElB8C,EAAYF,SAAWE,EAAYG,QAAQrD,GAG3C0B,EAAa4B,MAGd,MAAOJ,GAAYF,SAKpB,MAHCjB,GAAM,GAAIwB,OAAM,YAAcnD,EAAc,gBAGtC,KAUR,QAASoD,GAAaC,GACrB,GAAIhH,GACHiH,EACAC,EAGAC,EACAC,EAHAC,KACAC,IAMD,KAFAL,EAAgBD,EAAaT,SAASrB,cAEjClF,EAAI,EAAGA,EAAIiH,EAAc9G,OAAQH,IACrCoH,EAAeH,EAAcjH,GAEvB,qBAAuBgH,KAC5BA,EAAaK,sBAGdF,EAA0BH,EAAaK,kBACvCH,EAAehC,EAAUkC,GAGrBA,IAAgBE,GACnBhC,EAAM,GAAIwB,OAAM,aAAeM,EAAe,6CACpCF,GAELC,EAAwBC,KAC5BD,EAAwBC,GAAgBF,EAAaN,QAAQI,EAAaO,UAG3EF,EAAkBjH,KAAK+G,EAAwBC,KAE/C9B,EAAM,GAAIwB,OAAM,aAAeM,EAAe,gBAI/CE,EAAkBF,IAAgB,CAGnC,OAAOC,GAYR,QAASG,GAA2BC,EAAgBrG,EAASrB,GAC5D,GAAI2H,GAAW,GAAInI,GAAIkC,iBAAiBL,EAASrB,EAAS+E,EAAa5C,WACvEuF,GAAerH,KAAKsH,GACpBA,EAAS3E,eASV,QAAS4E,GAAmBX,GAC3B,GAAIS,GAAiBT,EAAaS,eACjCG,EAAkBb,EAAaC,EAGhCQ,GAA2BC,EAAgBT,EAAa5F,QAAS4F,EAAaT,SAG9E,KAAK,GAAIvG,GAAI,EAAGA,EAAI4H,EAAgBzH,OAAQH,IAC3CwH,EAA2BC,EAAgBT,EAAa5F,QAASwG,EAAgB5H,IAUnF,QAAS6H,GAAqBb,GAI7B,IAAK,GAFDS,GAAiBT,EAAaS,eAEzBzH,EAAI,EAAGA,EAAIyH,EAAetH,OAAQH,IAC1CyH,EAAezH,GAAGqD,cAGnB2D,GAAaS,kBASd,QAASK,GAAyB1G,GACjC,MAAO+D,GAAU/D,EAAQ2G,IAW1B,QAASC,GAAmBzB,EAAU9C,EAAMnD,GAGhB,OAAvBiG,EAAS0B,WAAoD,gBAAvB1B,GAAS0B,WAA0B1B,EAAS0B,UAAUxD,eAAehB,GAC9G8C,EAAS0B,UAAUxE,GAAM/C,KAAK6F,EAAUjG,GAGa,KAA3CoE,EAAQ6B,EAAS2B,aAAgBzE,IAC3C8C,EAAS0B,UAAUvH,KAAK6F,EAAU9C,EAAMnD,GA5V1C,GAAI6H,GAAkB,gBAElBrD,KACHC,KACAE,KACAD,KACAE,KACAC,KACAC,GAAc,EACdG,EAAqB,KAErBhC,EAAc,GAAIhE,GAAIC,WA0VvB,OAAO6E,GAAOd,GAWb6E,KAAM,SAASC,GAOd,MANAhE,GAAOS,EAAcuD,OAErB5I,KAAK6I,SAASC,SAASC,iBAEvB/I,KAAKY,KAAK,QACV+E,GAAc,EACP3F,MAORgJ,QAAS,WAKR,MAJAhJ,MAAKiJ,QAAQH,SAASC,iBAEtB3D,IAEOpF,MAcRkJ,UAAW,SAASvH,GACnB,GAAI4F,GAAec,EAAyB1G,EAC5C,OAA+B,gBAAjB4F,IASf4B,MAAO,SAASxH,GACf,GAEC4F,GACAO,EACAsB,EAJGC,EAAa3C,EAAc/E,GAC9B2H,EAAahE,EAAQ+D,EAKtB,KAAKC,EAEJ,MADAzD,GAAM,GAAIwB,OAAM,gBAAkBgC,EAAa,sBACxCrJ,IAGR,KAAKA,KAAKkJ,UAAUvH,GAAU,CAExBA,EAAQ2G,KACZ3G,EAAQ2G,GAAK,OAASe,EAAa,IAAMC,EAAWC,SAGrDD,EAAWC,UAEXzB,EAAU,GAAIhI,GAAI+D,QAAQ7D,KAAM2B,GAEhCyH,EAASE,EAAWnC,QAAQW,GAGvBzC,EAAaU,OACjBC,EAAoBoD,EAAQC,GAG7B9B,GACC8B,WAAYA,EACZvC,SAAUsC,EACVtB,QAASA,EACTnG,QAASA,EACTqG,mBAGDtC,EAAU/D,EAAQ2G,IAAMf,CAKxB,KAAK,GAFJiC,GADGrB,EAAkBb,EAAaC,GAG1BhH,EAAI,EAAGC,EAAM2H,EAAgBzH,OAAYF,EAAJD,EAASA,IACtDiJ,EAAmBrB,EAAgB5H,GACnCsG,EAAiB2C,EAAkB,OAIpC3C,GAAiBU,EAAaT,SAAU,QAGxCoB,EAAmBX,GAIpB,MAAOvH,OASRyJ,KAAM,SAAS9H,GACd,GAAI4F,GAAec,EAAyB1G,EAE5C,IAAK4F,EAOE,CAENa,EAAqBb,EAKrB,KAAK,GADDiC,GADArB,EAAkBb,EAAaC,GAE1BhH,EAAI4H,EAAgBzH,OAAS,EAAGH,GAAK,EAAGA,IAChDiJ,EAAmBrB,EAAgB5H,GACnCsG,EAAiB2C,EAAkB,UAGpC3C,GAAiBU,EAAaT,SAAU,iBAEjCpB,GAAU/D,EAAQ2G,QAnBzB,IAAIjD,EAAaU,MAEhB,MADAF,GAAM,GAAIwB,OAAM,kDAAoD1F,EAAQ2G,KACrEtI,IAoBT,OAAOA,OAQR6I,SAAU,SAASvH,GAGlB,IAAK,GAFDoI,GAAiB5J,EAAIiC,IAAIN,SAASH,EAAMoH,GAEnCnI,EAAI,EAAGC,EAAMkJ,EAAehJ,OAAYF,EAAJD,EAASA,IACrDP,KAAKmJ,MAAMO,EAAenJ,GAG3B,OAAOP,OAQRiJ,QAAS,SAAS3H,GAGjB,IAAK,GAFDoI,GAAiB5J,EAAIiC,IAAIN,SAASH,EAAMoH,GAEnCnI,EAAI,EAAGC,EAAMkJ,EAAehJ,OAAYF,EAAJD,EAASA,IACrDP,KAAKyJ,KAAKC,EAAenJ,GAG1B,OAAOP,OAcR2J,UAAW,SAASN,EAAYlC,GAC/B,MAAmC,mBAAxB7B,GAAQ+D,IAClBxD,EAAM,GAAIwB,OAAM,UAAYgC,EAAa,6BAClCrJ,OAGRsF,EAAQ+D,IACPlC,QAASA,EACToC,QAAS,GAGHvJ,OAWRqE,gBAAiB,SAAS1C,EAASqC,GAElC,GAAIuD,GAAec,EAAyB1G,GACxCiI,EAAe,IAEnB,IAAIrC,GAAgBA,EAAasC,OAEhCD,EAAerC,EAAasC,WACtB,CAEN,GAAIC,GAAgBhK,EAAIiC,IAAIV,MAAMM,EAAS,+BAG3C,IAAImI,EACH,IACCF,EAAeG,KAAKC,MAAMF,EAAcG,MACvC,MAAOxF,GACRoB,EAAM,GAAIwB,OAAM,kBAAoB1F,EAAQ2G,GAAK,6BAK/Cf,IACHA,EAAasC,OAASD,GAIxB,MAAKA,GAEsB,mBAAT5F,GACV4F,EACG5F,IAAQ4F,GACXA,EAAa5F,GAEb,KANA,MAsBTkG,WAAY,SAAShG,EAAaiD,GAEjC,MAAqC,mBAA1B5B,GAASrB,IACnB2B,EAAM,GAAIwB,OAAM,WAAanD,EAAc,6BACpClE,OAGRuF,EAASrB,IACRiD,QAASA,EACTL,SAAU,MAGJ9G,OAQRiE,WAAYA,EAOZE,WAAY,SAASD,GACpB,MAAOqB,GAASP,eAAed,IAchCiG,YAAa,SAASxC,EAAcR,GACnC,MAAuC,mBAA5B1B,GAAUkC,IACpB9B,EAAM,GAAIwB,OAAM,YAAcM,EAAe,6BACtC3H,OAGRyF,EAAUkC,IACTR,QAASA,EACTL,SAAU,MAGJ9G,OAaR+D,UAAW,SAASC,EAAMnD,GACzB,GAAIN,GACH+H,EACAf,EACAiC,EACArB,CAED,KAAKG,IAAM5C,GAEV,GAAIA,EAAUV,eAAesD,GAQ5B,IAPAf,EAAe7B,EAAU4C,GAGzBC,EAAmBhB,EAAaT,SAAU9C,EAAMnD,GAGhDsH,EAAkBb,EAAaC,GAC1BhH,EAAI,EAAGA,EAAI4H,EAAgBzH,OAAQH,IACvCiJ,EAAmBrB,EAAgB5H,GACnCgI,EAAmBiB,EAAkBxF,EAAMnD,EAY9C,OALAb,MAAKY,KAAK,WACT6F,QAASzC,EACToG,YAAavJ,IAGPb,MAYRsE,UAAW,SAASN,GACnB,MAAIA,KAAQnE,GACJA,EAAOmE,GAEP,MAUTO,gBAAiB,SAASP,GACzB,MAAoB,mBAATA,GACHqB,EACGrB,IAAQqB,GACXA,EAAarB,GAEb,MASTqG,gBAAiB,SAASR,GACzB,MAAIlE,IACHE,EAAM,GAAIwB,OAAM,qEACTrH,OAGR4E,EAAOS,EAAcwE,GACd7J,OAaRsK,gBAAiB,SAASC,GACzBzE,EAAqByE,GAStB/F,YAAaqB,EASb2E,cAAe,SAAS3J,GACvB,GAAIwE,EAAaU,MAAO,CAEvB,GAAI0E,GAAgBzK,KAAKsE,UAAU,UAC/BmG,IAAiBA,EAAcC,MAClCD,EAAcC,KAAK7J,OAGpBiD,GAAYlD,KAAK,UAAWC,IAU9B8J,WAAY,SAAS9J,GACpB,GAAIwE,EAAaU,MAAO,CAEvB,GAAI0E,GAAgBzK,KAAKsE,UAAU,UAC/BmG,IAAiBA,EAAcG,MAClCH,EAAcG,KAAK/J,UASD,kBAAXgK,SAAyBA,OAAOC,IAE1CD,OAAO,QAAU,WAChB,MAAO/K,SAEF,IAAsB,gBAAXsJ,SAAiD,gBAAnBA,QAAO2B,QAEtD3B,OAAO2B,QAAUjL,MACX,CAEND,EAAOC,IAAMD,EAAOC,OAGpB,KAAK,GAAIkL,KAAOlL,GACXA,EAAIkF,eAAegG,KACtBnL,EAAOC,IAAIkL,GAAOlL,EAAIkL,MAMN,mBAAXnL,QAAyBA,OAASG"}
--------------------------------------------------------------------------------
/Makefile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Build file
3 | * @author nzakas
4 | */
5 | /*global config, target, exec, echo, find, which, test, exit, mkdir*/
6 |
7 | 'use strict';
8 |
9 | //------------------------------------------------------------------------------
10 | // Requirements
11 | //------------------------------------------------------------------------------
12 |
13 | require('shelljs/make');
14 |
15 | var util = require('util'),
16 | path = require('path'),
17 | nodeCLI = require('shelljs-nodecli'),
18 | semver = require('semver'),
19 | dateformat = require('dateformat'),
20 | uglifyjs = require('uglify-js');
21 |
22 | //------------------------------------------------------------------------------
23 | // Data
24 | //------------------------------------------------------------------------------
25 |
26 | var NODE = 'node ', // intentional extra space
27 | NODE_MODULES = './node_modules/',
28 | DIST_DIR = './dist/',
29 |
30 | // Utilities - intentional extra space at the end of each string
31 | JSDOC = NODE + NODE_MODULES + 'jsdoc/jsdoc.js ',
32 |
33 | // Since our npm package name is actually 't3js'
34 | DIST_NAME = 't3',
35 | DIST_JQUERY_NAME = DIST_NAME + '-jquery',
36 | DIST_NATIVE_NAME = DIST_NAME + '-native',
37 |
38 | // Directories
39 | JS_DIRS = getSourceDirectories(),
40 |
41 | // Files
42 |
43 | SRC_JQUERY_FILES = ['lib/wrap-start.partial', 'lib/box.js', 'lib/event-target.js', 'lib/dom-jquery.js', 'lib/dom-event-delegate.js', 'lib/context.js', 'lib/application.js', 'lib/wrap-end.partial'],
44 | SRC_NATIVE_FILES = ['lib/wrap-start.partial', 'lib/box.js', 'lib/event-target.js', 'lib/dom-native.js', 'lib/dom-event-delegate.js', 'lib/context.js', 'lib/application.js', 'lib/wrap-end.partial'],
45 | TESTING_JQUERY_FILES = ['lib/wrap-start.partial', 'lib/box.js', 'lib/event-target.js', 'lib/dom-jquery.js', 'lib/dom-event-delegate.js', 'lib/application-stub.js', 'lib/test-service-provider.js', 'lib/wrap-end.partial'],
46 | TESTING_NATIVE_FILES = ['lib/wrap-start.partial', 'lib/box.js', 'lib/event-target.js', 'lib/dom-native.js', 'lib/dom-event-delegate.js', 'lib/application-stub.js', 'lib/test-service-provider.js', 'lib/wrap-end.partial'],
47 | JS_FILES = find(JS_DIRS).filter(fileType('js')).join(' '),
48 | TEST_FILES = find('tests/').filter(fileType('js')).join(' ');
49 |
50 | //------------------------------------------------------------------------------
51 | // Helpers
52 | //------------------------------------------------------------------------------
53 |
54 | /**
55 | * Checks if current repository has any uncommitted changes
56 | * @return {boolean}
57 | */
58 | function isDirectoryClean() {
59 | var fatalState = config.fatal; // save current fatal state
60 | config.fatal = false;
61 | var isUnstagedChanges = exec('git diff --exit-code', {silent:true}).code;
62 | var isStagedChanged = exec('git diff --cached --exit-code', {silent:true}).code;
63 | config.fatal = fatalState; // restore fatal state
64 | return !(isUnstagedChanges || isStagedChanged);
65 | }
66 |
67 | /**
68 | * Executes a Node CLI and exits with a non-zero exit code if the
69 | * CLI execution returns a non-zero exit code. Otherwise, it does
70 | * not exit.
71 | * @param {...string} [args] Arguments to pass to the Node CLI utility.
72 | * @returns {void}
73 | * @private
74 | */
75 | function nodeExec(args) {
76 | args = arguments; // make linting happy
77 | var code = nodeCLI.exec.apply(nodeCLI, args).code;
78 | if (code !== 0) {
79 | exit(code);
80 | }
81 | }
82 |
83 | /**
84 | * Runs exec() but exits if the exit code is non-zero.
85 | * @param {string} cmd The command to execute.
86 | * @returns {void}
87 | * @private
88 | */
89 | function execOrExit(cmd) {
90 | var code = exec(cmd).code;
91 | if (code !== 0) {
92 | exit(code);
93 | }
94 | }
95 |
96 | /**
97 | * Generates a function that matches files with a particular extension.
98 | * @param {string} extension The file extension (i.e. 'js')
99 | * @returns {Function} The function to pass into a filter method.
100 | * @private
101 | */
102 | function fileType(extension) {
103 | return function(filename) {
104 | return filename.substring(filename.lastIndexOf('.') + 1) === extension;
105 | };
106 | }
107 |
108 | /**
109 | * Determines which directories are present that might have JavaScript files.
110 | * @returns {string[]} An array of directories that exist.
111 | * @private
112 | */
113 | function getSourceDirectories() {
114 | var dirs = [ 'lib', 'src', 'app' ],
115 | result = [];
116 |
117 | dirs.forEach(function(dir) {
118 | if (test('-d', dir)) {
119 | result.push(dir);
120 | }
121 | });
122 |
123 | return result;
124 | }
125 |
126 | /**
127 | * Gets the git tags that represent versions.
128 | * @returns {string[]} An array of tags in the git repo.
129 | * @private
130 | */
131 | function getVersionTags() {
132 | var tags = exec('git tag', { silent: true }).output.trim().split(/\n/g);
133 |
134 | return tags.reduce(function(list, tag) {
135 | if (semver.valid(tag)) {
136 | list.push(tag);
137 | }
138 | return list;
139 | }, []).sort(semver.compare);
140 | }
141 |
142 | /**
143 | * Verifies that common module loaders can be used with dist files
144 | * @returns {void}
145 | * @private
146 | */
147 | function validateModuleLoading() {
148 | var t3js = require(DIST_DIR + DIST_NAME + '.js');
149 |
150 | // Validate CommonJS
151 | if (!t3js || !('Application' in t3js)) {
152 | echo('ERROR: The dist file is not wrapped correctly for CommonJS');
153 | exit(1);
154 | }
155 | }
156 |
157 | /**
158 | * Updates the README links with the latest version
159 | * @param string version The latest version string
160 | * @returns {void}
161 | * @private
162 | */
163 | function updateReadme(version) {
164 | // Copy to temp file
165 | cat('README.md').to('README.tmp');
166 |
167 | // Replace Version String
168 | sed('-i', /\/box\/t3js\/v([^/])+/g, '/box/t3js/' + version, 'README.tmp');
169 |
170 | // Replace README
171 | rm('README.md');
172 | mv('README.tmp', 'README.md');
173 | }
174 |
175 | /**
176 | * Generate distribution files for a single package
177 | * @param Object config The distribution configuration
178 | * @returns {void}
179 | * @private
180 | */
181 | function generateDistFiles(config) {
182 | // Delete package.json from the cache since it can get updated by npm version
183 | delete require.cache[require.resolve('./package.json')];
184 |
185 | var pkg = require('./package.json'),
186 | distFilename = DIST_DIR + config.name + '.js',
187 | minDistFilename = distFilename.replace(/\.js$/, '.min.js'),
188 | minDistSourcemapFilename = minDistFilename + '.map',
189 | distTestingFilename = DIST_DIR + config.name + '-testing' + '.js';
190 |
191 | // Add copyrights and version info
192 | var versionComment = '/*! ' + config.name + ' v' + pkg.version + ' */\n',
193 | testingVersionComment = '/*! ' + config.name + '-testing v' + pkg.version + ' */\n',
194 | copyrightComment = cat('./config/copyright.txt');
195 |
196 | // concatenate files together and add version/copyright notices
197 | (versionComment + copyrightComment + cat(config.files)).to(distFilename);
198 | (testingVersionComment + copyrightComment + cat(config.testingFiles)).to(distTestingFilename);
199 |
200 | // create minified version with source maps
201 | var result = uglifyjs.minify(distFilename, {
202 | output: {
203 | comments: /^!/
204 | },
205 | outSourceMap: path.basename(minDistSourcemapFilename)
206 | });
207 | result.code.to(minDistFilename);
208 | result.map.to(minDistSourcemapFilename);
209 |
210 | // create filenames with version in them
211 | cp(distFilename, distFilename.replace('.js', '-' + pkg.version + '.js'));
212 | cp(minDistFilename, minDistFilename.replace('.min.js', '-' + pkg.version + '.min.js'));
213 | cp(distTestingFilename, distTestingFilename.replace('.js', '-' + pkg.version + '.js'));
214 | }
215 |
216 | /**
217 | * Generate all distribution files
218 | * @private
219 | */
220 | function dist() {
221 | if (test('-d', DIST_DIR)) {
222 | rm('-r', DIST_DIR + '*');
223 | } else {
224 | mkdir(DIST_DIR);
225 | }
226 |
227 | [{
228 | name: DIST_NATIVE_NAME,
229 | files: SRC_NATIVE_FILES,
230 | testingFiles: TESTING_NATIVE_FILES
231 | }, {
232 | name: DIST_JQUERY_NAME,
233 | files: SRC_JQUERY_FILES,
234 | testingFiles: TESTING_JQUERY_FILES
235 | }, {
236 | name: DIST_NAME,
237 | files: SRC_NATIVE_FILES,
238 | testingFiles: TESTING_NATIVE_FILES
239 | }].forEach(function(config){
240 | generateDistFiles(config);
241 | });
242 | }
243 |
244 | /**
245 | * Creates a release version tag and pushes to origin.
246 | * @param {string} type The type of release to do (patch, minor, major)
247 | * @returns {void}
248 | */
249 | function release(type) {
250 |
251 | // 'npm version' needs a clean repository to run
252 | if (!isDirectoryClean()) {
253 | echo('RELEASE ERROR: Working directory must be clean to push release!');
254 | exit(1);
255 | }
256 |
257 | echo('Running tests');
258 | target.test();
259 |
260 | // Step 1: Create the new version
261 | echo('Creating new version');
262 | var newVersion = exec('npm version ' + type).output.trim();
263 |
264 | // Step 2: Generate files
265 | echo('Generating dist files');
266 | dist();
267 |
268 | echo('Generating changelog');
269 | target.changelog();
270 |
271 | echo('Updating README');
272 | updateReadme(newVersion);
273 |
274 | // Step 3: Validate CommonJS wrapping
275 | echo('Validating module loading');
276 | validateModuleLoading();
277 |
278 | // Step 4: Add files to current commit
279 | execOrExit('git add -A');
280 | execOrExit('git commit --amend --no-edit');
281 |
282 | // Step 5: reset the git tag to the latest commit
283 | execOrExit('git tag -f ' + newVersion);
284 |
285 | // Step 6: publish to git
286 | echo('Pushing to github');
287 | execOrExit('git push origin master --tags');
288 |
289 | // Step 7: publish to npm
290 | echo('Publishing to NPM');
291 | execOrExit('npm publish');
292 |
293 | // Step 8: Update version number in docs site
294 | echo('Updating documentation site');
295 | execOrExit('git checkout gh-pages');
296 | ('version: ' + newVersion).to('_data/t3.yml');
297 | execOrExit('git commit -am "Update version number to ' + newVersion + '"');
298 | execOrExit('git fetch origin && git rebase origin/gh-pages && git push origin gh-pages');
299 |
300 | // Step 9: Switch back to master
301 | execOrExit('git checkout master');
302 |
303 | // Step 10: Party time
304 | }
305 |
306 |
307 | //------------------------------------------------------------------------------
308 | // Tasks
309 | //------------------------------------------------------------------------------
310 |
311 | target.all = function() {
312 | target.test();
313 | };
314 |
315 | target.lint = function() {
316 | echo('Validating JavaScript files');
317 | nodeExec('eslint', [JS_FILES, TEST_FILES].join(' '));
318 | };
319 |
320 | target.test = function() {
321 | target.lint();
322 |
323 | echo('Running browser tests');
324 | var code = exec('node ./node_modules/karma/bin/karma start config/karma-conf.js').code;
325 | if (code !== 0) {
326 | exit(code);
327 | }
328 |
329 | echo('Running Utilities tests');
330 | target['utils-test']();
331 |
332 | echo('Running API tests');
333 | target['api-test']();
334 | };
335 |
336 | target['utils-test'] = function() {
337 | var code = exec('node ./node_modules/karma/bin/karma start config/testing-utils-karma-conf.js').code;
338 | if (code !== 0) {
339 | exit(code);
340 | }
341 | };
342 |
343 | target['api-test'] = function() {
344 | // generate dist files that are used by api-test
345 | dist();
346 |
347 | nodeExec('mocha', './tests/api-test.js');
348 |
349 | // revert generated files
350 | execOrExit('git checkout dist');
351 | };
352 |
353 | target['test-watch'] = function() {
354 | echo('Watching files to run browser tests. Press Ctrl+C to exit.');
355 | var code = exec('node ./node_modules/karma/bin/karma start config/karma-conf.js --single-run=false --autoWatch').code;
356 | if (code !== 0) {
357 | exit(code);
358 | }
359 | };
360 |
361 | target.docs = function() {
362 | echo('Generating documentation');
363 | exec(JSDOC + '-d jsdoc ' + JS_DIRS.join(' '));
364 | echo('Documentation has been output to /jsdoc');
365 | };
366 |
367 | // Don't assign directly to dist since shelljs wraps this function
368 | target.dist = function() {
369 | dist();
370 | };
371 |
372 | target.changelog = function() {
373 |
374 | // get most recent two tags
375 | var tags = getVersionTags(),
376 | rangeTags = tags.slice(tags.length - 2),
377 | now = new Date(),
378 | timestamp = dateformat(now, 'mmmm d, yyyy');
379 |
380 | // output header
381 | (rangeTags[1] + ' - ' + timestamp + '\n').to('CHANGELOG.tmp');
382 |
383 | // get log statements
384 | var logs = exec('git log --pretty=format:"* %s (%an)" ' + rangeTags.join('..'), {silent: true}).output.split(/\n/g);
385 | logs = logs.filter(function(line) {
386 | return line.indexOf('Merge pull request') === -1 && line.indexOf('Merge branch') === -1;
387 | });
388 | logs.push(''); // to create empty lines
389 | logs.unshift('');
390 |
391 | // output log statements
392 | logs.join('\n').toEnd('CHANGELOG.tmp');
393 |
394 | // switch-o change-o
395 | cat('CHANGELOG.tmp', 'CHANGELOG.md').to('CHANGELOG.md.tmp');
396 | rm('CHANGELOG.tmp');
397 | rm('CHANGELOG.md');
398 | mv('CHANGELOG.md.tmp', 'CHANGELOG.md');
399 | };
400 |
401 | target.patch = function() {
402 | release('patch');
403 | };
404 |
405 | target.minor = function() {
406 | release('minor');
407 | };
408 |
409 | target.major = function() {
410 | release('major');
411 | };
412 |
--------------------------------------------------------------------------------