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 );