You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@senssoft.apache.org by ar...@apache.org on 2016/10/21 16:12:37 UTC
[44/48] incubator-senssoft-tap git commit: Cherry pick front end
bundle
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/containers/App.jsx
----------------------------------------------------------------------
diff --git a/public/containers/App.jsx b/public/containers/App.jsx
new file mode 100644
index 0000000..5110ee0
--- /dev/null
+++ b/public/containers/App.jsx
@@ -0,0 +1,40 @@
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+
+class App extends Component {
+ render() {
+ const { children, id, name, settings, results, users } = this.props;
+
+ return(
+ <div>
+ {React.cloneElement(children, {
+ id,
+ name,
+ settings,
+ results,
+ users,
+ })}
+ </div>
+ );
+ }
+}
+
+App.propTypes = {
+ id : PropTypes.number,
+ name : PropTypes.string,
+ settings : PropTypes.object,
+ results : PropTypes.object,
+ users : PropTypes.array,
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ id : state.app.id,
+ name : state.app.name,
+ settings : state.app.settings,
+ results : state.app.results,
+ users : state.app.users,
+ };
+}
+
+export default connect(mapStateToProps)(App);
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/containers/Main.jsx
----------------------------------------------------------------------
diff --git a/public/containers/Main.jsx b/public/containers/Main.jsx
new file mode 100644
index 0000000..e03ecca
--- /dev/null
+++ b/public/containers/Main.jsx
@@ -0,0 +1,89 @@
+import React, { Component, PropTypes } from 'react';
+import { Link } from 'react-router';
+import { connect } from 'react-redux';
+import { logIn, logOut } from '../actions';
+
+class Main extends Component {
+ constructor(props) {
+ super(props);
+ this.handleLogin = this.handleLogin.bind(this);
+ this.handleLogout = this.handleLogout.bind(this);
+ }
+
+ handleLogin () {
+ this.props.logIn();
+ }
+
+ handleLogout () {
+ this.props.logOut();
+ }
+
+ render () {
+ const { children, isLoggedIn, username } = this.props;
+ return (
+ <div id='main-container'>
+ <div className='site-header'>
+ <div className='ui brown inverted padded top fixed borderless menu'>
+ <div className='ui container'>
+
+ <Link to='/'>
+ <h3 className='ui inverted header item'>Tap</h3>
+ </Link>
+
+ <div className='right menu'>
+ {
+ // <div className='item' onClick={isLoggedIn ? this.handleLogout : this.handleLogin }>
+ // {isLoggedIn ? 'Log Out' : 'Log In'}
+ // </div>
+ }
+ <div className='item'>
+ <Link to='/user'>
+ Profile
+ </Link>
+ </div>
+ <div className='item'>
+ <Link to='/user/settings'>
+ Settings
+ </Link>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <div id='page-wrapper'>
+ {children}
+ </div>
+
+ <div className='site-footer'>
+ <div className='ui container'>
+ <div className='ui footer page brown inverted segment'>
+ <div className='ui center aligned container'>
+ <div className='footer-text'>Copyright Tap 2016</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+Main.propTypes = {
+ children : PropTypes.node,
+ isLoggedIn : PropTypes.bool.isRequired,
+ username : PropTypes.string,
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ isLoggedIn : state.auth.isLoggedIn,
+ email : state.auth.isLoggedIn ? state.user.email : null,
+ };
+}
+
+export default connect(mapStateToProps, {
+ logIn,
+ logOut,
+})(Main);
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/containers/Org.jsx
----------------------------------------------------------------------
diff --git a/public/containers/Org.jsx b/public/containers/Org.jsx
new file mode 100644
index 0000000..7c90080
--- /dev/null
+++ b/public/containers/Org.jsx
@@ -0,0 +1,43 @@
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+
+class Org extends Component {
+ render() {
+ const { children, id, name, description, users, apps, settings } = this.props;
+
+ return (
+ <div>
+ {React.cloneElement(children, {
+ id,
+ name,
+ description,
+ users,
+ apps,
+ settings,
+ })}
+ </div>
+ );
+ }
+}
+
+Org.propTypes = {
+ id : PropTypes.number,
+ name : PropTypes.string,
+ description : PropTypes.string,
+ users : PropTypes.array,
+ apps : PropTypes.array,
+ settings : PropTypes.object,
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ id : state.org.id,
+ name : state.org.name,
+ description : state.org.description,
+ users : state.org.users,
+ apps : state.org.apps,
+ settings : state.org.settings,
+ };
+}
+
+export default connect(mapStateToProps)(Org);
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/containers/User.jsx
----------------------------------------------------------------------
diff --git a/public/containers/User.jsx b/public/containers/User.jsx
new file mode 100644
index 0000000..76bb291
--- /dev/null
+++ b/public/containers/User.jsx
@@ -0,0 +1,37 @@
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+
+class User extends Component {
+ render() {
+ const { children, email, settings, orgs, apps } = this.props;
+
+ return (
+ <div>
+ {React.cloneElement(children, {
+ email,
+ settings,
+ orgs,
+ apps,
+ })}
+ </div>
+ );
+ }
+}
+
+User.propTypes = {
+ email : PropTypes.string.isRequired,
+ settings : PropTypes.object.isRequired,
+ orgs : PropTypes.array,
+ apps : PropTypes.array,
+};
+
+function mapStateToProps(state, ownProps) {
+ return {
+ email : state.user.email,
+ settings : state.user.settings,
+ orgs : state.user.orgs,
+ apps : state.user.apps,
+ };
+}
+
+export default connect(mapStateToProps)(User);
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/index.jsx
----------------------------------------------------------------------
diff --git a/public/index.jsx b/public/index.jsx
new file mode 100644
index 0000000..0a94470
--- /dev/null
+++ b/public/index.jsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { render } from 'react-dom';
+import { Router, Route, IndexRoute, Link, browserHistory } from 'react-router';
+import { Provider } from 'react-redux';
+
+import configureStore from './store/configureStore';
+
+import Main from './containers/Main';
+import Home from './components/Home';
+// import Login from './containers/Login';
+// import Logout from './containers/Logout';
+// import SignUp from './containers/SignUp';
+import User from './containers/User';
+import UserProfile from './components/UserProfile';
+import UserSettings from './components/UserSettings';
+import Org from './containers/Org';
+import OrgProfile from './components/OrgProfile';
+import OrgSettings from './components/OrgSettings';
+// import OrgNew from './containers/OrgNew';
+import App from './containers/App';
+import AppProfile from './components/AppProfile';
+import AppSettings from './components/AppSettings';
+import AppResults from './components/AppResults';
+// import AppNew from './containers/AppNew';
+
+const store = configureStore();
+
+render((
+ <Provider store={store}>
+ <Router history={browserHistory}>
+ <Route path='/' component={Main}>
+ <IndexRoute component={Home} />
+
+ {
+ // <Route path='login' component={Login} />
+ // <Route path='logout' component={Logout} />
+ // <Route path='signup' component={SignUp} />
+ }
+
+ <Route path='user' component={User}>
+ <IndexRoute component={UserProfile} />
+ <Route path='settings' component={UserSettings} />
+ </Route>
+
+ <Route path='org/:id' component={Org}>
+ <IndexRoute component={OrgProfile} />
+ <Route path='settings' component={OrgSettings} />
+ </Route>
+
+ <Route path='app/:id' component={App}>
+ <IndexRoute component={AppProfile} />
+ <Route path='settings' component={AppSettings} />
+ <Route path='results' component={AppResults} />
+ </Route>
+
+ {
+ // <Route path='org/new' component={OrgNew} />
+ // <Route path='app/new' component={AppNew} />
+ }
+ </Route>
+ </Router>
+ </Provider>
+), document.getElementById('react-container'))
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/reducers/app.js
----------------------------------------------------------------------
diff --git a/public/reducers/app.js b/public/reducers/app.js
new file mode 100644
index 0000000..852345a
--- /dev/null
+++ b/public/reducers/app.js
@@ -0,0 +1,99 @@
+import { REQUEST_APP, RECEIVE_APP, REQUEST_APP_UPDATE, CONFIRM_APP_UPDATE, REQUEST_APP_RESULTS, RECEIVE_APP_RESULTS } from '../constants/ActionTypes';
+import merge from 'lodash/merge';
+
+import graphData from '../neon_graph.js';
+import countsData from '../neon_counts.js';
+
+const initialAppState = {
+ id : 7,
+ name : 'Ale',
+ settings : {
+ setting1 : 'a setting',
+ },
+ users : [
+ {
+ id : 1,
+ email : 'test@test.com'
+ },
+ {
+ id : 2,
+ email : 'test2@test.com'
+ },
+ ],
+ results : {
+ counts : countsData,
+ // counts : [
+ // {
+ // group : 'map',
+ // activities : [
+ // {
+ // id : 'zoom',
+ // count : 100,
+ // },
+ // {
+ // id : 'pan',
+ // count : 27,
+ // },
+ // {
+ // id : 'resize',
+ // count : 74,
+ // },
+ // ]
+ // },
+ // {
+ // group : 'linechart',
+ // activities : [
+ // {
+ // id : 'tooltip',
+ // count : 51,
+ // },
+ // {
+ // id : 'select',
+ // count : 88,
+ // },
+ // {
+ // id : 'resize',
+ // count : 12,
+ // },
+ // ]
+ // },
+ // {
+ // group : 'table',
+ // activities : [
+ // {
+ // id : 'reorder',
+ // count : 11,
+ // },
+ // {
+ // id : 'resize',
+ // count : 16,
+ // },
+ // {
+ // id : 'filter',
+ // count : 67,
+ // },
+ // ]
+ // },
+ // ],
+ graph : graphData,
+ },
+};
+
+export default function app (state = initialAppState, action) {
+ switch (action.type) {
+ case REQUEST_APP:
+ return merge({}, state, {});
+ case RECEIVE_APP:
+ return merge({}, state, action.app);
+ case REQUEST_APP_UPDATE:
+ return merge({}, state, action.update);
+ case CONFIRM_APP_UPDATE:
+ return merge({}, state, action.saved);
+ case REQUEST_APP_RESULTS:
+ return merge({}, state, {});
+ case RECEIVE_APP_RESULTS:
+ return merge({}, state, action.results);
+ default:
+ return state;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/reducers/auth.js
----------------------------------------------------------------------
diff --git a/public/reducers/auth.js b/public/reducers/auth.js
new file mode 100644
index 0000000..935323d
--- /dev/null
+++ b/public/reducers/auth.js
@@ -0,0 +1,17 @@
+import { LOG_IN, LOG_OUT } from '../constants/ActionTypes';
+import merge from 'lodash/merge';
+
+const initialAuthState = {
+ isLoggedIn : false,
+};
+
+export default function auth (state = initialAuthState, action) {
+ switch (action.type) {
+ case LOG_IN:
+ return merge({}, state, { isLoggedIn : true });
+ case LOG_OUT:
+ return merge({}, state, { isLoggedIn : false });
+ default:
+ return state;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/reducers/index.js
----------------------------------------------------------------------
diff --git a/public/reducers/index.js b/public/reducers/index.js
new file mode 100644
index 0000000..873c783
--- /dev/null
+++ b/public/reducers/index.js
@@ -0,0 +1,15 @@
+import { combineReducers } from 'redux';
+
+import auth from './auth';
+import user from './user';
+import org from './org';
+import app from './app';
+
+const rootReducer = combineReducers({
+ auth,
+ user,
+ org,
+ app
+});
+
+export default rootReducer;
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/reducers/org.js
----------------------------------------------------------------------
diff --git a/public/reducers/org.js b/public/reducers/org.js
new file mode 100644
index 0000000..320699d
--- /dev/null
+++ b/public/reducers/org.js
@@ -0,0 +1,46 @@
+import { REQUEST_ORG, RECEIVE_ORG, REQUEST_ORG_UPDATE, CONFIRM_ORG_UPDATE } from '../constants/ActionTypes';
+import merge from 'lodash/merge';
+
+const initialOrgState = {
+ id : 3,
+ name : 'Tap',
+ description : 'The Tap Group',
+ settings : {
+ setting1 : true
+ },
+ users : [
+ {
+ id : 1,
+ email : 'test@test.com'
+ },
+ {
+ id : 2,
+ email : 'test2@test.com'
+ }
+ ],
+ apps : [
+ {
+ id : 7,
+ name : 'Ale'
+ },
+ {
+ id : 1,
+ name : 'Stout (fake)'
+ }
+ ]
+};
+
+export default function org (state = initialOrgState, action) {
+ switch (action.type) {
+ case REQUEST_ORG:
+ return merge({}, state, {});
+ case RECEIVE_ORG:
+ return merge({}, state. action.org);
+ case REQUEST_ORG_UPDATE:
+ return merge({}, state, action.update);
+ case CONFIRM_ORG_UPDATE:
+ return merge({}, state, action.saved);
+ default:
+ return state;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/reducers/user.js
----------------------------------------------------------------------
diff --git a/public/reducers/user.js b/public/reducers/user.js
new file mode 100644
index 0000000..3900d2f
--- /dev/null
+++ b/public/reducers/user.js
@@ -0,0 +1,46 @@
+import { REQUEST_USER, RECEIVE_USER, REQUEST_USER_UPDATE, CONFIRM_USER_UPDATE } from '../constants/ActionTypes';
+import merge from 'lodash/merge';
+
+const initialUserState = {
+ email : 'test@test.com',
+ settings : {
+ setting1 : true,
+ setting2 : 'hello',
+ setting3 : 4,
+ },
+ orgs : [
+ {
+ id : 3,
+ name : 'Tap',
+ },
+ {
+ id : 2,
+ name : 'Distill (fake)',
+ }
+ ],
+ apps : [
+ {
+ id : 7,
+ name : 'Ale',
+ },
+ {
+ id : 1,
+ name : 'Stout (fake)',
+ },
+ ]
+};
+
+export default function user (state = initialUserState, action) {
+ switch (action.type) {
+ case REQUEST_USER:
+ return merge({}, state, {});
+ case RECEIVE_USER:
+ return merge({}, state, action.user);
+ case REQUEST_USER_UPDATE:
+ return merge({}, state, action.update);
+ case CONFIRM_USER_UPDATE:
+ return merge({}, state, action.saved);
+ default:
+ return state;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/public/store/configureStore.js
----------------------------------------------------------------------
diff --git a/public/store/configureStore.js b/public/store/configureStore.js
new file mode 100644
index 0000000..4a3baa5
--- /dev/null
+++ b/public/store/configureStore.js
@@ -0,0 +1,15 @@
+import { createStore, applyMiddleware } from 'redux';
+import thunk from 'redux-thunk';
+import logger from 'redux-logger';
+
+import rootReducer from '../reducers';
+
+export default function configureStore (preloadedState) {
+ const store = createStore(
+ rootReducer,
+ preloadedState,
+ applyMiddleware(thunk, logger())
+ );
+
+ return store;
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/semantic.json
----------------------------------------------------------------------
diff --git a/semantic.json b/semantic.json
new file mode 100644
index 0000000..edb31cc
--- /dev/null
+++ b/semantic.json
@@ -0,0 +1,22 @@
+{
+ "base": "semantic/",
+ "paths": {
+ "source": {
+ "config": "src/theme.config",
+ "definitions": "src/definitions/",
+ "site": "src/site/",
+ "themes": "src/themes/"
+ },
+ "output": {
+ "packaged": "dist/",
+ "uncompressed": "dist/components/",
+ "compressed": "dist/components/",
+ "themes": "dist/themes/"
+ },
+ "clean": "dist/"
+ },
+ "permission": false,
+ "autoInstall": false,
+ "rtl": false,
+ "version": "2.2.1"
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/semantic/gulpfile.js
----------------------------------------------------------------------
diff --git a/semantic/gulpfile.js b/semantic/gulpfile.js
new file mode 100644
index 0000000..bde1960
--- /dev/null
+++ b/semantic/gulpfile.js
@@ -0,0 +1,72 @@
+/*******************************
+ Set-up
+*******************************/
+
+var
+ gulp = require('gulp-help')(require('gulp')),
+
+ // read user config to know what task to load
+ config = require('./tasks/config/user'),
+
+ // watch changes
+ watch = require('./tasks/watch'),
+
+ // build all files
+ build = require('./tasks/build'),
+ buildJS = require('./tasks/build/javascript'),
+ buildCSS = require('./tasks/build/css'),
+ buildAssets = require('./tasks/build/assets'),
+
+ // utility
+ clean = require('./tasks/clean'),
+ version = require('./tasks/version'),
+
+ // docs tasks
+ serveDocs = require('./tasks/docs/serve'),
+ buildDocs = require('./tasks/docs/build'),
+
+ // rtl
+ buildRTL = require('./tasks/rtl/build'),
+ watchRTL = require('./tasks/rtl/watch')
+;
+
+
+/*******************************
+ Tasks
+*******************************/
+
+gulp.task('default', false, [
+ 'watch'
+]);
+
+gulp.task('watch', 'Watch for site/theme changes', watch);
+
+gulp.task('build', 'Builds all files from source', build);
+gulp.task('build-javascript', 'Builds all javascript from source', buildJS);
+gulp.task('build-css', 'Builds all css from source', buildCSS);
+gulp.task('build-assets', 'Copies all assets from source', buildAssets);
+
+gulp.task('clean', 'Clean dist folder', clean);
+gulp.task('version', 'Displays current version of Semantic', version);
+
+/*--------------
+ Docs
+---------------*/
+
+/*
+ Lets you serve files to a local documentation instance
+ https://github.com/Semantic-Org/Semantic-UI-Docs/
+*/
+
+gulp.task('serve-docs', 'Serve file changes to SUI Docs', serveDocs);
+gulp.task('build-docs', 'Build all files and add to SUI Docs', buildDocs);
+
+
+/*--------------
+ RTL
+---------------*/
+
+if(config.rtl) {
+ gulp.task('watch-rtl', 'Watch files as RTL', watchRTL);
+ gulp.task('build-rtl', 'Build all files as RTL', buildRTL);
+}
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/semantic/src/definitions/behaviors/api.js
----------------------------------------------------------------------
diff --git a/semantic/src/definitions/behaviors/api.js b/semantic/src/definitions/behaviors/api.js
new file mode 100644
index 0000000..578e201
--- /dev/null
+++ b/semantic/src/definitions/behaviors/api.js
@@ -0,0 +1,1167 @@
+/*!
+ * # Semantic UI - API
+ * http://github.com/semantic-org/semantic-ui/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+"use strict";
+
+var
+ window = (typeof window != 'undefined' && window.Math == Math)
+ ? window
+ : (typeof self != 'undefined' && self.Math == Math)
+ ? self
+ : Function('return this')()
+;
+
+$.api = $.fn.api = function(parameters) {
+
+ var
+ // use window context if none specified
+ $allModules = $.isFunction(this)
+ ? $(window)
+ : $(this),
+ moduleSelector = $allModules.selector || '',
+ time = new Date().getTime(),
+ performance = [],
+
+ query = arguments[0],
+ methodInvoked = (typeof query == 'string'),
+ queryArguments = [].slice.call(arguments, 1),
+
+ returnedValue
+ ;
+
+ $allModules
+ .each(function() {
+ var
+ settings = ( $.isPlainObject(parameters) )
+ ? $.extend(true, {}, $.fn.api.settings, parameters)
+ : $.extend({}, $.fn.api.settings),
+
+ // internal aliases
+ namespace = settings.namespace,
+ metadata = settings.metadata,
+ selector = settings.selector,
+ error = settings.error,
+ className = settings.className,
+
+ // define namespaces for modules
+ eventNamespace = '.' + namespace,
+ moduleNamespace = 'module-' + namespace,
+
+ // element that creates request
+ $module = $(this),
+ $form = $module.closest(selector.form),
+
+ // context used for state
+ $context = (settings.stateContext)
+ ? $(settings.stateContext)
+ : $module,
+
+ // request details
+ ajaxSettings,
+ requestSettings,
+ url,
+ data,
+ requestStartTime,
+
+ // standard module
+ element = this,
+ context = $context[0],
+ instance = $module.data(moduleNamespace),
+ module
+ ;
+
+ module = {
+
+ initialize: function() {
+ if(!methodInvoked) {
+ module.bind.events();
+ }
+ module.instantiate();
+ },
+
+ instantiate: function() {
+ module.verbose('Storing instance of module', module);
+ instance = module;
+ $module
+ .data(moduleNamespace, instance)
+ ;
+ },
+
+ destroy: function() {
+ module.verbose('Destroying previous module for', element);
+ $module
+ .removeData(moduleNamespace)
+ .off(eventNamespace)
+ ;
+ },
+
+ bind: {
+ events: function() {
+ var
+ triggerEvent = module.get.event()
+ ;
+ if( triggerEvent ) {
+ module.verbose('Attaching API events to element', triggerEvent);
+ $module
+ .on(triggerEvent + eventNamespace, module.event.trigger)
+ ;
+ }
+ else if(settings.on == 'now') {
+ module.debug('Querying API endpoint immediately');
+ module.query();
+ }
+ }
+ },
+
+ decode: {
+ json: function(response) {
+ if(response !== undefined && typeof response == 'string') {
+ try {
+ response = JSON.parse(response);
+ }
+ catch(e) {
+ // isnt json string
+ }
+ }
+ return response;
+ }
+ },
+
+ read: {
+ cachedResponse: function(url) {
+ var
+ response
+ ;
+ if(window.Storage === undefined) {
+ module.error(error.noStorage);
+ return;
+ }
+ response = sessionStorage.getItem(url);
+ module.debug('Using cached response', url, response);
+ response = module.decode.json(response);
+ return response;
+ }
+ },
+ write: {
+ cachedResponse: function(url, response) {
+ if(response && response === '') {
+ module.debug('Response empty, not caching', response);
+ return;
+ }
+ if(window.Storage === undefined) {
+ module.error(error.noStorage);
+ return;
+ }
+ if( $.isPlainObject(response) ) {
+ response = JSON.stringify(response);
+ }
+ sessionStorage.setItem(url, response);
+ module.verbose('Storing cached response for url', url, response);
+ }
+ },
+
+ query: function() {
+
+ if(module.is.disabled()) {
+ module.debug('Element is disabled API request aborted');
+ return;
+ }
+
+ if(module.is.loading()) {
+ if(settings.interruptRequests) {
+ module.debug('Interrupting previous request');
+ module.abort();
+ }
+ else {
+ module.debug('Cancelling request, previous request is still pending');
+ return;
+ }
+ }
+
+ // pass element metadata to url (value, text)
+ if(settings.defaultData) {
+ $.extend(true, settings.urlData, module.get.defaultData());
+ }
+
+ // Add form content
+ if(settings.serializeForm) {
+ settings.data = module.add.formData(settings.data);
+ }
+
+ // call beforesend and get any settings changes
+ requestSettings = module.get.settings();
+
+ // check if before send cancelled request
+ if(requestSettings === false) {
+ module.cancelled = true;
+ module.error(error.beforeSend);
+ return;
+ }
+ else {
+ module.cancelled = false;
+ }
+
+ // get url
+ url = module.get.templatedURL();
+
+ if(!url && !module.is.mocked()) {
+ module.error(error.missingURL);
+ return;
+ }
+
+ // replace variables
+ url = module.add.urlData( url );
+ // missing url parameters
+ if( !url && !module.is.mocked()) {
+ return;
+ }
+
+ requestSettings.url = settings.base + url;
+
+ // look for jQuery ajax parameters in settings
+ ajaxSettings = $.extend(true, {}, settings, {
+ type : settings.method || settings.type,
+ data : data,
+ url : settings.base + url,
+ beforeSend : settings.beforeXHR,
+ success : function() {},
+ failure : function() {},
+ complete : function() {}
+ });
+
+ module.debug('Querying URL', ajaxSettings.url);
+ module.verbose('Using AJAX settings', ajaxSettings);
+ if(settings.cache === 'local' && module.read.cachedResponse(url)) {
+ module.debug('Response returned from local cache');
+ module.request = module.create.request();
+ module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
+ return;
+ }
+
+ if( !settings.throttle ) {
+ module.debug('Sending request', data, ajaxSettings.method);
+ module.send.request();
+ }
+ else {
+ if(!settings.throttleFirstRequest && !module.timer) {
+ module.debug('Sending request', data, ajaxSettings.method);
+ module.send.request();
+ module.timer = setTimeout(function(){}, settings.throttle);
+ }
+ else {
+ module.debug('Throttling request', settings.throttle);
+ clearTimeout(module.timer);
+ module.timer = setTimeout(function() {
+ if(module.timer) {
+ delete module.timer;
+ }
+ module.debug('Sending throttled request', data, ajaxSettings.method);
+ module.send.request();
+ }, settings.throttle);
+ }
+ }
+
+ },
+
+ should: {
+ removeError: function() {
+ return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
+ }
+ },
+
+ is: {
+ disabled: function() {
+ return ($module.filter(selector.disabled).length > 0);
+ },
+ expectingJSON: function() {
+ return settings.dataType === 'json' || settings.dataType === 'jsonp';
+ },
+ form: function() {
+ return $module.is('form') || $context.is('form');
+ },
+ mocked: function() {
+ return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
+ },
+ input: function() {
+ return $module.is('input');
+ },
+ loading: function() {
+ return (module.request)
+ ? (module.request.state() == 'pending')
+ : false
+ ;
+ },
+ abortedRequest: function(xhr) {
+ if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
+ module.verbose('XHR request determined to be aborted');
+ return true;
+ }
+ else {
+ module.verbose('XHR request was not aborted');
+ return false;
+ }
+ },
+ validResponse: function(response) {
+ if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
+ module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
+ return true;
+ }
+ module.debug('Checking JSON returned success', settings.successTest, response);
+ if( settings.successTest(response) ) {
+ module.debug('Response passed success test', response);
+ return true;
+ }
+ else {
+ module.debug('Response failed success test', response);
+ return false;
+ }
+ }
+ },
+
+ was: {
+ cancelled: function() {
+ return (module.cancelled || false);
+ },
+ succesful: function() {
+ return (module.request && module.request.state() == 'resolved');
+ },
+ failure: function() {
+ return (module.request && module.request.state() == 'rejected');
+ },
+ complete: function() {
+ return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
+ }
+ },
+
+ add: {
+ urlData: function(url, urlData) {
+ var
+ requiredVariables,
+ optionalVariables
+ ;
+ if(url) {
+ requiredVariables = url.match(settings.regExp.required);
+ optionalVariables = url.match(settings.regExp.optional);
+ urlData = urlData || settings.urlData;
+ if(requiredVariables) {
+ module.debug('Looking for required URL variables', requiredVariables);
+ $.each(requiredVariables, function(index, templatedString) {
+ var
+ // allow legacy {$var} style
+ variable = (templatedString.indexOf('$') !== -1)
+ ? templatedString.substr(2, templatedString.length - 3)
+ : templatedString.substr(1, templatedString.length - 2),
+ value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
+ ? urlData[variable]
+ : ($module.data(variable) !== undefined)
+ ? $module.data(variable)
+ : ($context.data(variable) !== undefined)
+ ? $context.data(variable)
+ : urlData[variable]
+ ;
+ // remove value
+ if(value === undefined) {
+ module.error(error.requiredParameter, variable, url);
+ url = false;
+ return false;
+ }
+ else {
+ module.verbose('Found required variable', variable, value);
+ value = (settings.encodeParameters)
+ ? module.get.urlEncodedValue(value)
+ : value
+ ;
+ url = url.replace(templatedString, value);
+ }
+ });
+ }
+ if(optionalVariables) {
+ module.debug('Looking for optional URL variables', requiredVariables);
+ $.each(optionalVariables, function(index, templatedString) {
+ var
+ // allow legacy {/$var} style
+ variable = (templatedString.indexOf('$') !== -1)
+ ? templatedString.substr(3, templatedString.length - 4)
+ : templatedString.substr(2, templatedString.length - 3),
+ value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
+ ? urlData[variable]
+ : ($module.data(variable) !== undefined)
+ ? $module.data(variable)
+ : ($context.data(variable) !== undefined)
+ ? $context.data(variable)
+ : urlData[variable]
+ ;
+ // optional replacement
+ if(value !== undefined) {
+ module.verbose('Optional variable Found', variable, value);
+ url = url.replace(templatedString, value);
+ }
+ else {
+ module.verbose('Optional variable not found', variable);
+ // remove preceding slash if set
+ if(url.indexOf('/' + templatedString) !== -1) {
+ url = url.replace('/' + templatedString, '');
+ }
+ else {
+ url = url.replace(templatedString, '');
+ }
+ }
+ });
+ }
+ }
+ return url;
+ },
+ formData: function(data) {
+ var
+ canSerialize = ($.fn.serializeObject !== undefined),
+ formData = (canSerialize)
+ ? $form.serializeObject()
+ : $form.serialize(),
+ hasOtherData
+ ;
+ data = data || settings.data;
+ hasOtherData = $.isPlainObject(data);
+
+ if(hasOtherData) {
+ if(canSerialize) {
+ module.debug('Extending existing data with form data', data, formData);
+ data = $.extend(true, {}, data, formData);
+ }
+ else {
+ module.error(error.missingSerialize);
+ module.debug('Cant extend data. Replacing data with form data', data, formData);
+ data = formData;
+ }
+ }
+ else {
+ module.debug('Adding form data', formData);
+ data = formData;
+ }
+ return data;
+ }
+ },
+
+ send: {
+ request: function() {
+ module.set.loading();
+ module.request = module.create.request();
+ if( module.is.mocked() ) {
+ module.mockedXHR = module.create.mockedXHR();
+ }
+ else {
+ module.xhr = module.create.xhr();
+ }
+ settings.onRequest.call(context, module.request, module.xhr);
+ }
+ },
+
+ event: {
+ trigger: function(event) {
+ module.query();
+ if(event.type == 'submit' || event.type == 'click') {
+ event.preventDefault();
+ }
+ },
+ xhr: {
+ always: function() {
+ // nothing special
+ },
+ done: function(response, textStatus, xhr) {
+ var
+ context = this,
+ elapsedTime = (new Date().getTime() - requestStartTime),
+ timeLeft = (settings.loadingDuration - elapsedTime),
+ translatedResponse = ( $.isFunction(settings.onResponse) )
+ ? module.is.expectingJSON()
+ ? settings.onResponse.call(context, $.extend(true, {}, response))
+ : settings.onResponse.call(context, response)
+ : false
+ ;
+ timeLeft = (timeLeft > 0)
+ ? timeLeft
+ : 0
+ ;
+ if(translatedResponse) {
+ module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
+ response = translatedResponse;
+ }
+ if(timeLeft > 0) {
+ module.debug('Response completed early delaying state change by', timeLeft);
+ }
+ setTimeout(function() {
+ if( module.is.validResponse(response) ) {
+ module.request.resolveWith(context, [response, xhr]);
+ }
+ else {
+ module.request.rejectWith(context, [xhr, 'invalid']);
+ }
+ }, timeLeft);
+ },
+ fail: function(xhr, status, httpMessage) {
+ var
+ context = this,
+ elapsedTime = (new Date().getTime() - requestStartTime),
+ timeLeft = (settings.loadingDuration - elapsedTime)
+ ;
+ timeLeft = (timeLeft > 0)
+ ? timeLeft
+ : 0
+ ;
+ if(timeLeft > 0) {
+ module.debug('Response completed early delaying state change by', timeLeft);
+ }
+ setTimeout(function() {
+ if( module.is.abortedRequest(xhr) ) {
+ module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
+ }
+ else {
+ module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
+ }
+ }, timeLeft);
+ }
+ },
+ request: {
+ done: function(response, xhr) {
+ module.debug('Successful API Response', response);
+ if(settings.cache === 'local' && url) {
+ module.write.cachedResponse(url, response);
+ module.debug('Saving server response locally', module.cache);
+ }
+ settings.onSuccess.call(context, response, $module, xhr);
+ },
+ complete: function(firstParameter, secondParameter) {
+ var
+ xhr,
+ response
+ ;
+ // have to guess callback parameters based on request success
+ if( module.was.succesful() ) {
+ response = firstParameter;
+ xhr = secondParameter;
+ }
+ else {
+ xhr = firstParameter;
+ response = module.get.responseFromXHR(xhr);
+ }
+ module.remove.loading();
+ settings.onComplete.call(context, response, $module, xhr);
+ },
+ fail: function(xhr, status, httpMessage) {
+ var
+ // pull response from xhr if available
+ response = module.get.responseFromXHR(xhr),
+ errorMessage = module.get.errorFromRequest(response, status, httpMessage)
+ ;
+ if(status == 'aborted') {
+ module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
+ settings.onAbort.call(context, status, $module, xhr);
+ return true;
+ }
+ else if(status == 'invalid') {
+ module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
+ }
+ else if(status == 'error') {
+ if(xhr !== undefined) {
+ module.debug('XHR produced a server error', status, httpMessage);
+ // make sure we have an error to display to console
+ if( xhr.status != 200 && httpMessage !== undefined && httpMessage !== '') {
+ module.error(error.statusMessage + httpMessage, ajaxSettings.url);
+ }
+ settings.onError.call(context, errorMessage, $module, xhr);
+ }
+ }
+
+ if(settings.errorDuration && status !== 'aborted') {
+ module.debug('Adding error state');
+ module.set.error();
+ if( module.should.removeError() ) {
+ setTimeout(module.remove.error, settings.errorDuration);
+ }
+ }
+ module.debug('API Request failed', errorMessage, xhr);
+ settings.onFailure.call(context, response, $module, xhr);
+ }
+ }
+ },
+
+ create: {
+
+ request: function() {
+ // api request promise
+ return $.Deferred()
+ .always(module.event.request.complete)
+ .done(module.event.request.done)
+ .fail(module.event.request.fail)
+ ;
+ },
+
+ mockedXHR: function () {
+ var
+ // xhr does not simulate these properties of xhr but must return them
+ textStatus = false,
+ status = false,
+ httpMessage = false,
+ responder = settings.mockResponse || settings.response,
+ asyncResponder = settings.mockResponseAsync || settings.responseAsync,
+ asyncCallback,
+ response,
+ mockedXHR
+ ;
+
+ mockedXHR = $.Deferred()
+ .always(module.event.xhr.complete)
+ .done(module.event.xhr.done)
+ .fail(module.event.xhr.fail)
+ ;
+
+ if(responder) {
+ if( $.isFunction(responder) ) {
+ module.debug('Using specified synchronous callback', responder);
+ response = responder.call(context, requestSettings);
+ }
+ else {
+ module.debug('Using settings specified response', responder);
+ response = responder;
+ }
+ // simulating response
+ mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
+ }
+ else if( $.isFunction(asyncResponder) ) {
+ asyncCallback = function(response) {
+ module.debug('Async callback returned response', response);
+
+ if(response) {
+ mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
+ }
+ else {
+ mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
+ }
+ };
+ module.debug('Using specified async response callback', asyncResponder);
+ asyncResponder.call(context, requestSettings, asyncCallback);
+ }
+ return mockedXHR;
+ },
+
+ xhr: function() {
+ var
+ xhr
+ ;
+ // ajax request promise
+ xhr = $.ajax(ajaxSettings)
+ .always(module.event.xhr.always)
+ .done(module.event.xhr.done)
+ .fail(module.event.xhr.fail)
+ ;
+ module.verbose('Created server request', xhr, ajaxSettings);
+ return xhr;
+ }
+ },
+
+ set: {
+ error: function() {
+ module.verbose('Adding error state to element', $context);
+ $context.addClass(className.error);
+ },
+ loading: function() {
+ module.verbose('Adding loading state to element', $context);
+ $context.addClass(className.loading);
+ requestStartTime = new Date().getTime();
+ }
+ },
+
+ remove: {
+ error: function() {
+ module.verbose('Removing error state from element', $context);
+ $context.removeClass(className.error);
+ },
+ loading: function() {
+ module.verbose('Removing loading state from element', $context);
+ $context.removeClass(className.loading);
+ }
+ },
+
+ get: {
+ responseFromXHR: function(xhr) {
+ return $.isPlainObject(xhr)
+ ? (module.is.expectingJSON())
+ ? module.decode.json(xhr.responseText)
+ : xhr.responseText
+ : false
+ ;
+ },
+ errorFromRequest: function(response, status, httpMessage) {
+ return ($.isPlainObject(response) && response.error !== undefined)
+ ? response.error // use json error message
+ : (settings.error[status] !== undefined) // use server error message
+ ? settings.error[status]
+ : httpMessage
+ ;
+ },
+ request: function() {
+ return module.request || false;
+ },
+ xhr: function() {
+ return module.xhr || false;
+ },
+ settings: function() {
+ var
+ runSettings
+ ;
+ runSettings = settings.beforeSend.call(context, settings);
+ if(runSettings) {
+ if(runSettings.success !== undefined) {
+ module.debug('Legacy success callback detected', runSettings);
+ module.error(error.legacyParameters, runSettings.success);
+ runSettings.onSuccess = runSettings.success;
+ }
+ if(runSettings.failure !== undefined) {
+ module.debug('Legacy failure callback detected', runSettings);
+ module.error(error.legacyParameters, runSettings.failure);
+ runSettings.onFailure = runSettings.failure;
+ }
+ if(runSettings.complete !== undefined) {
+ module.debug('Legacy complete callback detected', runSettings);
+ module.error(error.legacyParameters, runSettings.complete);
+ runSettings.onComplete = runSettings.complete;
+ }
+ }
+ if(runSettings === undefined) {
+ module.error(error.noReturnedValue);
+ }
+ if(runSettings === false) {
+ return runSettings;
+ }
+ return (runSettings !== undefined)
+ ? $.extend(true, {}, runSettings)
+ : $.extend(true, {}, settings)
+ ;
+ },
+ urlEncodedValue: function(value) {
+ var
+ decodedValue = window.decodeURIComponent(value),
+ encodedValue = window.encodeURIComponent(value),
+ alreadyEncoded = (decodedValue !== value)
+ ;
+ if(alreadyEncoded) {
+ module.debug('URL value is already encoded, avoiding double encoding', value);
+ return value;
+ }
+ module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
+ return encodedValue;
+ },
+ defaultData: function() {
+ var
+ data = {}
+ ;
+ if( !$.isWindow(element) ) {
+ if( module.is.input() ) {
+ data.value = $module.val();
+ }
+ else if( module.is.form() ) {
+
+ }
+ else {
+ data.text = $module.text();
+ }
+ }
+ return data;
+ },
+ event: function() {
+ if( $.isWindow(element) || settings.on == 'now' ) {
+ module.debug('API called without element, no events attached');
+ return false;
+ }
+ else if(settings.on == 'auto') {
+ if( $module.is('input') ) {
+ return (element.oninput !== undefined)
+ ? 'input'
+ : (element.onpropertychange !== undefined)
+ ? 'propertychange'
+ : 'keyup'
+ ;
+ }
+ else if( $module.is('form') ) {
+ return 'submit';
+ }
+ else {
+ return 'click';
+ }
+ }
+ else {
+ return settings.on;
+ }
+ },
+ templatedURL: function(action) {
+ action = action || $module.data(metadata.action) || settings.action || false;
+ url = $module.data(metadata.url) || settings.url || false;
+ if(url) {
+ module.debug('Using specified url', url);
+ return url;
+ }
+ if(action) {
+ module.debug('Looking up url for action', action, settings.api);
+ if(settings.api[action] === undefined && !module.is.mocked()) {
+ module.error(error.missingAction, settings.action, settings.api);
+ return;
+ }
+ url = settings.api[action];
+ }
+ else if( module.is.form() ) {
+ url = $module.attr('action') || $context.attr('action') || false;
+ module.debug('No url or action specified, defaulting to form action', url);
+ }
+ return url;
+ }
+ },
+
+ abort: function() {
+ var
+ xhr = module.get.xhr()
+ ;
+ if( xhr && xhr.state() !== 'resolved') {
+ module.debug('Cancelling API request');
+ xhr.abort();
+ }
+ },
+
+ // reset state
+ reset: function() {
+ module.remove.error();
+ module.remove.loading();
+ },
+
+ setting: function(name, value) {
+ module.debug('Changing setting', name, value);
+ if( $.isPlainObject(name) ) {
+ $.extend(true, settings, name);
+ }
+ else if(value !== undefined) {
+ if($.isPlainObject(settings[name])) {
+ $.extend(true, settings[name], value);
+ }
+ else {
+ settings[name] = value;
+ }
+ }
+ else {
+ return settings[name];
+ }
+ },
+ internal: function(name, value) {
+ if( $.isPlainObject(name) ) {
+ $.extend(true, module, name);
+ }
+ else if(value !== undefined) {
+ module[name] = value;
+ }
+ else {
+ return module[name];
+ }
+ },
+ debug: function() {
+ if(!settings.silent && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.debug.apply(console, arguments);
+ }
+ }
+ },
+ verbose: function() {
+ if(!settings.silent && settings.verbose && settings.debug) {
+ if(settings.performance) {
+ module.performance.log(arguments);
+ }
+ else {
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+ module.verbose.apply(console, arguments);
+ }
+ }
+ },
+ error: function() {
+ if(!settings.silent) {
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+ module.error.apply(console, arguments);
+ }
+ },
+ performance: {
+ log: function(message) {
+ var
+ currentTime,
+ executionTime,
+ previousTime
+ ;
+ if(settings.performance) {
+ currentTime = new Date().getTime();
+ previousTime = time || currentTime;
+ executionTime = currentTime - previousTime;
+ time = currentTime;
+ performance.push({
+ 'Name' : message[0],
+ 'Arguments' : [].slice.call(message, 1) || '',
+ //'Element' : element,
+ 'Execution Time' : executionTime
+ });
+ }
+ clearTimeout(module.performance.timer);
+ module.performance.timer = setTimeout(module.performance.display, 500);
+ },
+ display: function() {
+ var
+ title = settings.name + ':',
+ totalTime = 0
+ ;
+ time = false;
+ clearTimeout(module.performance.timer);
+ $.each(performance, function(index, data) {
+ totalTime += data['Execution Time'];
+ });
+ title += ' ' + totalTime + 'ms';
+ if(moduleSelector) {
+ title += ' \'' + moduleSelector + '\'';
+ }
+ if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+ console.groupCollapsed(title);
+ if(console.table) {
+ console.table(performance);
+ }
+ else {
+ $.each(performance, function(index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+ });
+ }
+ console.groupEnd();
+ }
+ performance = [];
+ }
+ },
+ invoke: function(query, passedArguments, context) {
+ var
+ object = instance,
+ maxDepth,
+ found,
+ response
+ ;
+ passedArguments = passedArguments || queryArguments;
+ context = element || context;
+ if(typeof query == 'string' && object !== undefined) {
+ query = query.split(/[\. ]/);
+ maxDepth = query.length - 1;
+ $.each(query, function(depth, value) {
+ var camelCaseValue = (depth != maxDepth)
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+ : query
+ ;
+ if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+ object = object[camelCaseValue];
+ }
+ else if( object[camelCaseValue] !== undefined ) {
+ found = object[camelCaseValue];
+ return false;
+ }
+ else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+ object = object[value];
+ }
+ else if( object[value] !== undefined ) {
+ found = object[value];
+ return false;
+ }
+ else {
+ module.error(error.method, query);
+ return false;
+ }
+ });
+ }
+ if ( $.isFunction( found ) ) {
+ response = found.apply(context, passedArguments);
+ }
+ else if(found !== undefined) {
+ response = found;
+ }
+ if($.isArray(returnedValue)) {
+ returnedValue.push(response);
+ }
+ else if(returnedValue !== undefined) {
+ returnedValue = [returnedValue, response];
+ }
+ else if(response !== undefined) {
+ returnedValue = response;
+ }
+ return found;
+ }
+ };
+
+ if(methodInvoked) {
+ if(instance === undefined) {
+ module.initialize();
+ }
+ module.invoke(query);
+ }
+ else {
+ if(instance !== undefined) {
+ instance.invoke('destroy');
+ }
+ module.initialize();
+ }
+ })
+ ;
+
+ return (returnedValue !== undefined)
+ ? returnedValue
+ : this
+ ;
+};
+
+$.api.settings = {
+
+ name : 'API',
+ namespace : 'api',
+
+ debug : false,
+ verbose : false,
+ performance : true,
+
+ // object containing all templates endpoints
+ api : {},
+
+ // whether to cache responses
+ cache : true,
+
+ // whether new requests should abort previous requests
+ interruptRequests : true,
+
+ // event binding
+ on : 'auto',
+
+ // context for applying state classes
+ stateContext : false,
+
+ // duration for loading state
+ loadingDuration : 0,
+
+ // whether to hide errors after a period of time
+ hideError : 'auto',
+
+ // duration for error state
+ errorDuration : 2000,
+
+ // whether parameters should be encoded with encodeURIComponent
+ encodeParameters : true,
+
+ // API action to use
+ action : false,
+
+ // templated URL to use
+ url : false,
+
+ // base URL to apply to all endpoints
+ base : '',
+
+ // data that will
+ urlData : {},
+
+ // whether to add default data to url data
+ defaultData : true,
+
+ // whether to serialize closest form
+ serializeForm : false,
+
+ // how long to wait before request should occur
+ throttle : 0,
+
+ // whether to throttle first request or only repeated
+ throttleFirstRequest : true,
+
+ // standard ajax settings
+ method : 'get',
+ data : {},
+ dataType : 'json',
+
+ // mock response
+ mockResponse : false,
+ mockResponseAsync : false,
+
+ // aliases for mock
+ response : false,
+ responseAsync : false,
+
+ // callbacks before request
+ beforeSend : function(settings) { return settings; },
+ beforeXHR : function(xhr) {},
+ onRequest : function(promise, xhr) {},
+
+ // after request
+ onResponse : false, // function(response) { },
+
+ // response was successful, if JSON passed validation
+ onSuccess : function(response, $module) {},
+
+ // request finished without aborting
+ onComplete : function(response, $module) {},
+
+ // failed JSON success test
+ onFailure : function(response, $module) {},
+
+ // server error
+ onError : function(errorMessage, $module) {},
+
+ // request aborted
+ onAbort : function(errorMessage, $module) {},
+
+ successTest : false,
+
+ // errors
+ error : {
+ beforeSend : 'The before send function has aborted the request',
+ error : 'There was an error with your request',
+ exitConditions : 'API Request Aborted. Exit conditions met',
+ JSONParse : 'JSON could not be parsed during error handling',
+ legacyParameters : 'You are using legacy API success callback names',
+ method : 'The method you called is not defined',
+ missingAction : 'API action used but no url was defined',
+ missingSerialize : 'jquery-serialize-object is required to add form data to an existing data object',
+ missingURL : 'No URL specified for api event',
+ noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
+ noStorage : 'Caching responses locally requires session storage',
+ parseError : 'There was an error parsing your request',
+ requiredParameter : 'Missing a required URL parameter: ',
+ statusMessage : 'Server gave an error: ',
+ timeout : 'Your request timed out'
+ },
+
+ regExp : {
+ required : /\{\$*[A-z0-9]+\}/g,
+ optional : /\{\/\$*[A-z0-9]+\}/g,
+ },
+
+ className: {
+ loading : 'loading',
+ error : 'error'
+ },
+
+ selector: {
+ disabled : '.disabled',
+ form : 'form'
+ },
+
+ metadata: {
+ action : 'action',
+ url : 'url'
+ }
+};
+
+
+
+})( jQuery, window, document );
http://git-wip-us.apache.org/repos/asf/incubator-senssoft-tap/blob/db97ec6f/semantic/src/definitions/behaviors/colorize.js
----------------------------------------------------------------------
diff --git a/semantic/src/definitions/behaviors/colorize.js b/semantic/src/definitions/behaviors/colorize.js
new file mode 100644
index 0000000..31efb1c
--- /dev/null
+++ b/semantic/src/definitions/behaviors/colorize.js
@@ -0,0 +1,280 @@
+/*!
+ * # Semantic UI - Colorize
+ * http://github.com/semantic-org/semantic-ui/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+"use strict";
+
+window = (typeof window != 'undefined' && window.Math == Math)
+ ? window
+ : (typeof self != 'undefined' && self.Math == Math)
+ ? self
+ : Function('return this')()
+;
+
+$.fn.colorize = function(parameters) {
+ var
+ settings = ( $.isPlainObject(parameters) )
+ ? $.extend(true, {}, $.fn.colorize.settings, parameters)
+ : $.extend({}, $.fn.colorize.settings),
+ // hoist arguments
+ moduleArguments = arguments || false
+ ;
+ $(this)
+ .each(function(instanceIndex) {
+
+ var
+ $module = $(this),
+
+ mainCanvas = $('<canvas />')[0],
+ imageCanvas = $('<canvas />')[0],
+ overlayCanvas = $('<canvas />')[0],
+
+ backgroundImage = new Image(),
+
+ // defs
+ mainContext,
+ imageContext,
+ overlayContext,
+
+ image,
+ imageName,
+
+ width,
+ height,
+
+ // shortcuts
+ colors = settings.colors,
+ paths = settings.paths,
+ namespace = settings.namespace,
+ error = settings.error,
+
+ // boilerplate
+ instance = $module.data('module-' + namespace),
+ module
+ ;
+
+ module = {
+
+ checkPreconditions: function() {
+ module.debug('Checking pre-conditions');
+
+ if( !$.isPlainObject(colors) || $.isEmptyObject(colors) ) {
+ module.error(error.undefinedColors);
+ return false;
+ }
+ return true;
+ },
+
+ async: function(callback) {
+ if(settings.async) {
+ setTimeout(callback, 0);
+ }
+ else {
+ callback();
+ }
+ },
+
+ getMetadata: function() {
+ module.debug('Grabbing metadata');
+ image = $module.data('image') || settings.image || undefined;
+ imageName = $module.data('name') || settings.name || instanceIndex;
+ width = settings.width || $module.width();
+ height = settings.height || $module.height();
+ if(width === 0 || height === 0) {
+ module.error(error.undefinedSize);
+ }
+ },
+
+ initialize: function() {
+ module.debug('Initializing with colors', colors);
+ if( module.checkPreconditions() ) {
+
+ module.async(function() {
+ module.getMetadata();
+ module.canvas.create();
+
+ module.draw.image(function() {
+ module.draw.colors();
+ module.canvas.merge();
+ });
+ $module
+ .data('module-' + namespace, module)
+ ;
+ });
+ }
+ },
+
+ redraw: function() {
+ module.debug('Redrawing image');
+ module.async(function() {
+ module.canvas.clear();
+ module.draw.colors();
+ module.canvas.merge();
+ });
+ },
+
+ change: {
+ color: function(colorName, color) {
+ module.debug('Changing color', colorName);
+ if(colors[colorName] === undefined) {
+ module.error(error.missingColor);
+ return false;
+ }
+ colors[colorName] = color;
+ module.redraw();
+ }
+ },
+
+ canvas: {
+ create: function() {
+ module.debug('Creating canvases');
+
+ mainCanvas.width = width;
+ mainCanvas.height = height;
+ imageCanvas.width = width;
+ imageCanvas.height = height;
+ overlayCanvas.width = width;
+ overlayCanvas.height = height;
+
+ mainContext = mainCanvas.getContext('2d');
+ imageContext = imageCanvas.getContext('2d');
+ overlayContext = overlayCanvas.getContext('2d');
+
+ $module
+ .append( mainCanvas )
+ ;
+ mainContext = $module.children('canvas')[0].getContext('2d');
+ },
+ clear: function(context) {
+ module.debug('Clearing canvas');
+ overlayContext.fillStyle = '#FFFFFF';
+ overlayContext.fillRect(0, 0, width, height);
+ },
+ merge: function() {
+ if( !$.isFunction(mainContext.blendOnto) ) {
+ module.error(error.missingPlugin);
+ return;
+ }
+ mainContext.putImageData( imageContext.getImageData(0, 0, width, height), 0, 0);
+ overlayContext.blendOnto(mainContext, 'multiply');
+ }
+ },
+
+ draw: {
+
+ image: function(callback) {
+ module.debug('Drawing image');
+ callback = callback || function(){};
+ if(image) {
+ backgroundImage.src = image;
+ backgroundImage.onload = function() {
+ imageContext.drawImage(backgroundImage, 0, 0);
+ callback();
+ };
+ }
+ else {
+ module.error(error.noImage);
+ callback();
+ }
+ },
+
+ colors: function() {
+ module.debug('Drawing color overlays', colors);
+ $.each(colors, function(colorName, color) {
+ settings.onDraw(overlayContext, imageName, colorName, color);
+ });
+ }
+
+ },
+
+ debug: function(message, variableName) {
+ if(settings.debug) {
+ if(variableName !== undefined) {
+ console.info(settings.name + ': ' + message, variableName);
+ }
+ else {
+ console.info(settings.name + ': ' + message);
+ }
+ }
+ },
+ error: function(errorMessage) {
+ console.warn(settings.name + ': ' + errorMessage);
+ },
+ invoke: function(methodName, context, methodArguments) {
+ var
+ method
+ ;
+ methodArguments = methodArguments || Array.prototype.slice.call( arguments, 2 );
+
+ if(typeof methodName == 'string' && instance !== undefined) {
+ methodName = methodName.split('.');
+ $.each(methodName, function(index, name) {
+ if( $.isPlainObject( instance[name] ) ) {
+ instance = instance[name];
+ return true;
+ }
+ else if( $.isFunction( instance[name] ) ) {
+ method = instance[name];
+ return true;
+ }
+ module.error(settings.error.method);
+ return false;
+ });
+ }
+ return ( $.isFunction( method ) )
+ ? method.apply(context, methodArguments)
+ : false
+ ;
+ }
+
+ };
+ if(instance !== undefined && moduleArguments) {
+ // simpler than invoke realizing to invoke itself (and losing scope due prototype.call()
+ if(moduleArguments[0] == 'invoke') {
+ moduleArguments = Array.prototype.slice.call( moduleArguments, 1 );
+ }
+ return module.invoke(moduleArguments[0], this, Array.prototype.slice.call( moduleArguments, 1 ) );
+ }
+ // initializing
+ module.initialize();
+ })
+ ;
+ return this;
+};
+
+$.fn.colorize.settings = {
+ name : 'Image Colorizer',
+ debug : true,
+ namespace : 'colorize',
+
+ onDraw : function(overlayContext, imageName, colorName, color) {},
+
+ // whether to block execution while updating canvas
+ async : true,
+ // object containing names and default values of color regions
+ colors : {},
+
+ metadata: {
+ image : 'image',
+ name : 'name'
+ },
+
+ error: {
+ noImage : 'No tracing image specified',
+ undefinedColors : 'No default colors specified.',
+ missingColor : 'Attempted to change color that does not exist',
+ missingPlugin : 'Blend onto plug-in must be included',
+ undefinedHeight : 'The width or height of image canvas could not be automatically determined. Please specify a height.'
+ }
+
+};
+
+})( jQuery, window, document );