<commit_before>import React, { Component } from 'react';
import {
  Editor,
  EditorState,
} from 'draft-js';

import createCompositeDecorator from './createCompositeDecorator';
import moveSelectionToEnd from './moveSelectionToEnd';
import proxies from './proxies';
import * as defaultKeyBindingPlugin from './defaultKeyBindingPlugin';
import { List } from 'immutable';

/**
 * The main editor component
 */
class PluginEditor extends Component {

  static propTypes = {
    editorState: React.PropTypes.object.isRequired,
    onChange: React.PropTypes.func.isRequired,
    plugins: React.PropTypes.array,
    defaultKeyBindings: React.PropTypes.bool,
    customStyleMap: React.PropTypes.object,
    decorators: React.PropTypes.array,
  };

  static defaultProps = {
    defaultKeyBindings: true,
    customStyleMap: {},
    plugins: [],
    decorators: [],
  };

  constructor(props) {
    super(props);

    this.plugins = [this.props, ...this.resolvePlugins()];
    for (const plugin of this.plugins) {
      if (typeof plugin.initialize !== 'function') continue;
      plugin.initialize({ ...this });
    }

    // attach proxy methods like `focus` or `blur`
    for (const method of proxies) {
      this[method] = (...args) => (
        this.refs.editor[method](...args)
      );
    }
  }

  componentWillMount() {
    const compositeDecorator = createCompositeDecorator(
      this.resolveDecorators(),
      this.getEditorState,
      this.onChange);
    const _editorState = EditorState.set(this.props.editorState, { decorator: compositeDecorator });
    this.onChange(moveSelectionToEnd(_editorState));
  }

  // Cycle through the plugins, changing the editor state with what the plugins
  // changed (or didn't)
  onChange = (editorState) => {
    let newEditorState = editorState;
    this.resolvePlugins().forEach((plugin) => {
      if (plugin.onChange) {
        newEditorState = plugin.onChange(newEditorState, { ...this });
      }
    });

    if (this.props.onChange) {
      this.props.onChange(newEditorState, { ...this });
    }
  };

  setEditorState = this.onChange;
  getEditorState = () => this.props.editorState;

  createEventHooks = (methodName, plugins) => (...args) => {
    const newArgs = [].slice.apply(args);
    newArgs.push({ ...this });
    for (const plugin of plugins) {
      if (typeof plugin[methodName] !== 'function') continue;
      const result = plugin[methodName](...newArgs);
      if (result === true) return true;
    }

    return false;
  };

  createFnHooks = (methodName, plugins) => (...args) => {
    const newArgs = [].slice.apply(args);

    newArgs.push({ ...this });

    if (methodName === 'blockRendererFn') {
      let block = { props: {} };
      let decorators = [];
      for (const plugin of plugins) {
        if (typeof plugin[methodName] !== 'function') continue;
        const result = plugin[methodName](...newArgs);
        if (result !== undefined && result !== null) {
          const { decorators: pluginDecorators, props: pluginProps, ...pluginRest } = result; // eslint-disable-line no-use-before-define
          const { props, ...rest } = block; // eslint-disable-line no-use-before-define
          if (pluginDecorators) decorators = [...decorators, ...pluginDecorators];
          block = { ...rest, ...pluginRest, props: { ...props, ...pluginProps } };
        }
      }

      if (block.component) {
        decorators.forEach(decorator => { block.component = decorator(block.component); });
        return block;
      }

      return false;
    } else if (methodName === 'blockStyleFn') {
      let styles;
      for (const plugin of plugins) {
        if (typeof plugin[methodName] !== 'function') continue;
        const result = plugin[methodName](...newArgs);
        if (result !== undefined) {
          styles = (styles ? (`${styles} `) : '') + result;
        }
      }

      return styles || false;
    }

    for (const plugin of plugins) {
      if (typeof plugin[methodName] !== 'function') continue;
      const result = plugin[methodName](...newArgs);
      if (result !== undefined) {
        return result;
      }
    }

    return false;
  };

  createPluginHooks = () => {
    const pluginHooks = {};
    const eventHookKeys = [];
    const fnHookKeys = [];
    const plugins = [this.props, ...this.resolvePlugins()];

    plugins.forEach((plugin) => {
      Object.keys(plugin).forEach((attrName) => {
        if (attrName === 'onChange') return;

        // if `attrName` has been added as a hook key already, ignore this one
        if (eventHookKeys.indexOf(attrName) !== -1 || fnHookKeys.indexOf(attrName) !== -1) return;

        const isEventHookKey = attrName.indexOf('on') === 0 || attrName.indexOf('handle') === 0;
        if (isEventHookKey) {
          eventHookKeys.push(attrName);
          return;
        }

        // checks if `attrName` ends with 'Fn'
        const isFnHookKey = (attrName.length - 2 === attrName.indexOf('Fn'));
        if (isFnHookKey) {
          fnHookKeys.push(attrName);
        }
      });
    });

    eventHookKeys.forEach((attrName) => {
      pluginHooks[attrName] = this.createEventHooks(attrName, plugins);
    });

    fnHookKeys.forEach((attrName) => {
      pluginHooks[attrName] = this.createFnHooks(attrName, plugins);
    });

    return pluginHooks;
  };

  resolvePlugins = () => {
    const plugins = this.props.plugins.slice(0);
    if (this.props.defaultKeyBindings) {
      plugins.push(defaultKeyBindingPlugin);
    }

    return plugins;
  };

  resolveDecorators = () => {
    const { decorators, plugins } = this.props;
    return List([{ decorators }, ...plugins])
      .filter((plugin) => plugin.decorators !== undefined)
      .flatMap((plugin) => plugin.decorators);
  };

  resolveCustomStyleMap = () => (
    this.props.plugins
     .filter(plug => plug.customStyleMap !== undefined)
     .map(plug => plug.customStyleMap)
     .concat([this.props.customStyleMap])
     .reduce((styles, style) => (
       {
         ...styles,
         ...style,
       }
     ), {})
  );

  resolveAccessibilityProps = () => {
    let accessibilityProps = {};
    const plugins = [this.props, ...this.resolvePlugins()];
    for (const plugin of plugins) {
      if (typeof plugin.getAccessibilityProps !== 'function') continue;
      const props = plugin.getAccessibilityProps();
      const popupProps = {};

      if (accessibilityProps.ariaHasPopup === undefined) {
        popupProps.ariaHasPopup = props.ariaHasPopup;
      } else if (props.ariaHasPopup === 'true') {
        popupProps.ariaHasPopup = 'true';
      }

      if (accessibilityProps.ariaExpanded === undefined) {
        popupProps.ariaExpanded = props.ariaExpanded;
      } else if (props.ariaExpanded === 'true') {
        popupProps.ariaExpanded = 'true';
      }

      accessibilityProps = {
        ...accessibilityProps,
        ...props,
        ...popupProps,
      };
    }

    return accessibilityProps;
  };

  render() {
    const pluginHooks = this.createPluginHooks();
    const customStyleMap = this.resolveCustomStyleMap();
    const accessibilityProps = this.resolveAccessibilityProps();
    return (
      <Editor
        { ...this.props }
        { ...accessibilityProps }
        { ...pluginHooks }
        customStyleMap={ customStyleMap }
        onChange={ this.onChange }
        editorState={ this.props.editorState }
        ref="editor"
      />
    );
  }
}

export default PluginEditor;
<commit_msg>remove block decorator stuff<commit_after>import React, { Component } from 'react';
import {
  Editor,
  EditorState,
} from 'draft-js';

import createCompositeDecorator from './createCompositeDecorator';
import moveSelectionToEnd from './moveSelectionToEnd';
import proxies from './proxies';
import * as defaultKeyBindingPlugin from './defaultKeyBindingPlugin';
import { List } from 'immutable';

/**
 * The main editor component
 */
class PluginEditor extends Component {

  static propTypes = {
    editorState: React.PropTypes.object.isRequired,
    onChange: React.PropTypes.func.isRequired,
    plugins: React.PropTypes.array,
    defaultKeyBindings: React.PropTypes.bool,
    customStyleMap: React.PropTypes.object,
    decorators: React.PropTypes.array,
  };

  static defaultProps = {
    defaultKeyBindings: true,
    customStyleMap: {},
    plugins: [],
    decorators: [],
  };

  constructor(props) {
    super(props);

    this.plugins = [this.props, ...this.resolvePlugins()];
    for (const plugin of this.plugins) {
      if (typeof plugin.initialize !== 'function') continue;
      plugin.initialize({ ...this });
    }

    // attach proxy methods like `focus` or `blur`
    for (const method of proxies) {
      this[method] = (...args) => (
        this.refs.editor[method](...args)
      );
    }
  }

  componentWillMount() {
    const compositeDecorator = createCompositeDecorator(
      this.resolveDecorators(),
      this.getEditorState,
      this.onChange);
    const _editorState = EditorState.set(this.props.editorState, { decorator: compositeDecorator });
    this.onChange(moveSelectionToEnd(_editorState));
  }

  // Cycle through the plugins, changing the editor state with what the plugins
  // changed (or didn't)
  onChange = (editorState) => {
    let newEditorState = editorState;
    this.resolvePlugins().forEach((plugin) => {
      if (plugin.onChange) {
        newEditorState = plugin.onChange(newEditorState, { ...this });
      }
    });

    if (this.props.onChange) {
      this.props.onChange(newEditorState, { ...this });
    }
  };

  setEditorState = this.onChange;
  getEditorState = () => this.props.editorState;

  createEventHooks = (methodName, plugins) => (...args) => {
    const newArgs = [].slice.apply(args);
    newArgs.push({ ...this });
    for (const plugin of plugins) {
      if (typeof plugin[methodName] !== 'function') continue;
      const result = plugin[methodName](...newArgs);
      if (result === true) return true;
    }

    return false;
  };

  createFnHooks = (methodName, plugins) => (...args) => {
    const newArgs = [].slice.apply(args);

    newArgs.push({ ...this });

    if (methodName === 'blockRendererFn') {
      let block = { props: {} };
      for (const plugin of plugins) {
        if (typeof plugin[methodName] !== 'function') continue;
        const result = plugin[methodName](...newArgs);
        if (result !== undefined && result !== null) {
          const { props: pluginProps, ...pluginRest } = result; // eslint-disable-line no-use-before-define
          const { props, ...rest } = block; // eslint-disable-line no-use-before-define
          block = { ...rest, ...pluginRest, props: { ...props, ...pluginProps } };
        }
      }

      return block.component ? block : false;
    } else if (methodName === 'blockStyleFn') {
      let styles;
      for (const plugin of plugins) {
        if (typeof plugin[methodName] !== 'function') continue;
        const result = plugin[methodName](...newArgs);
        if (result !== undefined) {
          styles = (styles ? (`${styles} `) : '') + result;
        }
      }

      return styles || false;
    }

    for (const plugin of plugins) {
      if (typeof plugin[methodName] !== 'function') continue;
      const result = plugin[methodName](...newArgs);
      if (result !== undefined) {
        return result;
      }
    }

    return false;
  };

  createPluginHooks = () => {
    const pluginHooks = {};
    const eventHookKeys = [];
    const fnHookKeys = [];
    const plugins = [this.props, ...this.resolvePlugins()];

    plugins.forEach((plugin) => {
      Object.keys(plugin).forEach((attrName) => {
        if (attrName === 'onChange') return;

        // if `attrName` has been added as a hook key already, ignore this one
        if (eventHookKeys.indexOf(attrName) !== -1 || fnHookKeys.indexOf(attrName) !== -1) return;

        const isEventHookKey = attrName.indexOf('on') === 0 || attrName.indexOf('handle') === 0;
        if (isEventHookKey) {
          eventHookKeys.push(attrName);
          return;
        }

        // checks if `attrName` ends with 'Fn'
        const isFnHookKey = (attrName.length - 2 === attrName.indexOf('Fn'));
        if (isFnHookKey) {
          fnHookKeys.push(attrName);
        }
      });
    });

    eventHookKeys.forEach((attrName) => {
      pluginHooks[attrName] = this.createEventHooks(attrName, plugins);
    });

    fnHookKeys.forEach((attrName) => {
      pluginHooks[attrName] = this.createFnHooks(attrName, plugins);
    });

    return pluginHooks;
  };

  resolvePlugins = () => {
    const plugins = this.props.plugins.slice(0);
    if (this.props.defaultKeyBindings) {
      plugins.push(defaultKeyBindingPlugin);
    }

    return plugins;
  };

  resolveDecorators = () => {
    const { decorators, plugins } = this.props;
    return List([{ decorators }, ...plugins])
      .filter((plugin) => plugin.decorators !== undefined)
      .flatMap((plugin) => plugin.decorators);
  };

  resolveCustomStyleMap = () => (
    this.props.plugins
     .filter(plug => plug.customStyleMap !== undefined)
     .map(plug => plug.customStyleMap)
     .concat([this.props.customStyleMap])
     .reduce((styles, style) => (
       {
         ...styles,
         ...style,
       }
     ), {})
  );

  resolveAccessibilityProps = () => {
    let accessibilityProps = {};
    const plugins = [this.props, ...this.resolvePlugins()];
    for (const plugin of plugins) {
      if (typeof plugin.getAccessibilityProps !== 'function') continue;
      const props = plugin.getAccessibilityProps();
      const popupProps = {};

      if (accessibilityProps.ariaHasPopup === undefined) {
        popupProps.ariaHasPopup = props.ariaHasPopup;
      } else if (props.ariaHasPopup === 'true') {
        popupProps.ariaHasPopup = 'true';
      }

      if (accessibilityProps.ariaExpanded === undefined) {
        popupProps.ariaExpanded = props.ariaExpanded;
      } else if (props.ariaExpanded === 'true') {
        popupProps.ariaExpanded = 'true';
      }

      accessibilityProps = {
        ...accessibilityProps,
        ...props,
        ...popupProps,
      };
    }

    return accessibilityProps;
  };

  render() {
    const pluginHooks = this.createPluginHooks();
    const customStyleMap = this.resolveCustomStyleMap();
    const accessibilityProps = this.resolveAccessibilityProps();
    return (
      <Editor
        { ...this.props }
        { ...accessibilityProps }
        { ...pluginHooks }
        customStyleMap={ customStyleMap }
        onChange={ this.onChange }
        editorState={ this.props.editorState }
        ref="editor"
      />
    );
  }
}

export default PluginEditor;
<|endoftext|>


<commit_before>'use strict';

angular
  .module('uSpreadsheetApp', [
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ngRoute'
  ])
  .config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  });
<commit_msg>Load bindonce into app<commit_after>'use strict';

angular
  .module('uSpreadsheetApp', [
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ngRoute',
    'pasvaz.bindonce'
  ])
  .config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  });
<|endoftext|>


<commit_before>'use strict';
/**
 * @ngdoc overview
 * @name sbAdminApp
 * @description
 * # sbAdminApp
 *
 * Main module of the application.
 */
angular
  .module(
      'sbAdminApp', [
          'oc.lazyLoad',
          'ui.router',
          'ui.bootstrap',
          'angular-loading-bar'
          // 'swaggerUi',
          //     'angular-rickshaw',
      ])
    .config(['$stateProvider','$urlRouterProvider','$ocLazyLoadProvider',function ($stateProvider,$urlRouterProvider,$ocLazyLoadProvider) {

    $ocLazyLoadProvider.config({
      debug:false,
      events:true,
    });

    $urlRouterProvider.otherwise('/dashboard/home');

    $stateProvider
      .state('dashboard', {
        url:'/dashboard',
        templateUrl: 'views/dashboard/main.html',
        resolve: {
            loadMyDirectives:function($ocLazyLoad){
                return $ocLazyLoad.load(
                {
                    name:'sbAdminApp',
                    files:[
                    'scripts/directives/header/header.js',
                    'scripts/directives/header/header-notification/header-notification.js',
                    'scripts/directives/sidebar/sidebar.js',
                    'scripts/directives/sidebar/sidebar-search/sidebar-search.js'
                    ]
                }),
                $ocLazyLoad.load(
                {
                   name:'toggle-switch',
                   files:["bower_components/angular-toggle-switch/angular-toggle-switch.min.js",
                          "bower_components/angular-toggle-switch/angular-toggle-switch.css"
                      ]
                }),
                $ocLazyLoad.load(
                {
                  name:'ngAnimate',
                  files:['bower_components/angular-animate/angular-animate.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngCookies',
                  files:['bower_components/angular-cookies/angular-cookies.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngResource',
                  files:['bower_components/angular-resource/angular-resource.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngSanitize',
                  files:['bower_components/angular-sanitize/angular-sanitize.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngTouch',
                  files:['bower_components/angular-touch/angular-touch.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
            }
        }
    })
      .state('dashboard.home',{
        url:'/home',
        controller: 'MainCtrl',
        templateUrl:'views/dashboard/home.html',
        resolve: {
          loadMyFiles:function($ocLazyLoad) {
            return $ocLazyLoad.load({
				 name:'chart.js',
              files:[
                'bower_components/angular-chart.js/dist/angular-chart.min.js',
                'bower_components/angular-chart.js/dist/angular-chart.css'
              ]
            }),
			 $ocLazyLoad.load({
              name:'sbAdminApp',
              files:[
              'scripts/controllers/main.js',
              'scripts/directives/timeline/timeline.js',
              'scripts/directives/notifications/notifications.js',
              'scripts/directives/chat/chat.js',
              'scripts/directives/dashboard/stats/stats.js',
			  'scripts/controllers/chartContoller.js',
              'scripts/controllers/table.js'
              ]
            })
          }
        }
      })
      .state('dashboard.form',{
        templateUrl:'views/form.html',
        url:'/form'
    })
      .state('dashboard.blank',{
        templateUrl:'views/pages/blank.html',
        url:'/blank'
    })
      .state('login',{
        templateUrl:'views/pages/login.html',
        url:'/login'
    })
      .state('dashboard.chart',{
        templateUrl:'views/chart.html',
        url:'/chart',
        controller:'ChartCtrl',
        resolve: {
          loadMyFile:function($ocLazyLoad) {
            return $ocLazyLoad.load({
              name:'chart.js',
              files:[
                'bower_components/angular-chart.js/dist/angular-chart.min.js',
                'bower_components/angular-chart.js/dist/angular-chart.css'
              ]
            }),
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:['scripts/controllers/chartContoller.js']
            })
          }
        }
    })
      .state('dashboard.table',{
        templateUrl:'views/table.html',
        url:'/table'
    })
      .state('dashboard.panels-wells',{
          templateUrl:'views/ui-elements/panels-wells.html',
          url:'/panels-wells'
      })
      .state('dashboard.buttons',{
        templateUrl:'views/ui-elements/buttons.html',
        url:'/buttons'
    })
      .state('dashboard.notifications',{
        templateUrl:'views/ui-elements/notifications.html',
        url:'/notifications'
    })
      .state('dashboard.typography',{
       templateUrl:'views/ui-elements/typography.html',
       url:'/typography'
   })
      .state('dashboard.icons',{
       templateUrl:'views/ui-elements/icons.html',
       url:'/icons'
   })
      .state('dashboard.grid',{
       templateUrl:'views/ui-elements/grid.html',
       url:'/grid'
   })
	.state('dashboard.sensorListTable',{
       templateUrl:'views/ui-elements/sensorListTable.html',
       url:'/sensorListTable',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }

   })
    .state('dashboard.customizeCloud',{
       templateUrl:'views/ui-elements/customizeCloud.html',
       url:'/customizeCloud',
       controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            }
        }
   })
    .state('dashboard.actuatorListTable',{
       templateUrl:'views/ui-elements/actuatorListTable.html',
       url:'/actuatorListTable',
       controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })
     .state('dashboard.triggerListTable',{
       templateUrl:'views/ui-elements/triggerListTable.html',
       url:'/triggerListTable',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })
     .state('dashboard.addTrigger',{
       templateUrl:'views/ui-elements/addTrigger.html',
       url:'/addTrigger',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js'
				]
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })
     .state('dashboard.deleteTrigger',{
       templateUrl:'views/ui-elements/deleteTrigger.html',
       url:'/deleteTrigger',
       controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js'
				]
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })

   .state('dashboard.errorListTable',{
       templateUrl:'views/ui-elements/errorListTable.html',
       url:'/errorListTable',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js'
				]
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })

  }]);
<commit_msg>Fixed Graph<commit_after>'use strict';
/**
 * @ngdoc overview
 * @name sbAdminApp
 * @description
 * # sbAdminApp
 *
 * Main module of the application.
 */
angular
  .module(
      'sbAdminApp', [
          'oc.lazyLoad',
          'ui.router',
          'ui.bootstrap',
          'angular-loading-bar',
          'angular-rickshaw'
      ])
    .config(['$stateProvider','$urlRouterProvider','$ocLazyLoadProvider',function ($stateProvider,$urlRouterProvider,$ocLazyLoadProvider) {

    $ocLazyLoadProvider.config({
      debug:false,
      events:true,
    });

    $urlRouterProvider.otherwise('/dashboard/home');

    $stateProvider
      .state('dashboard', {
        url:'/dashboard',
        templateUrl: 'views/dashboard/main.html',
        resolve: {
            loadMyDirectives:function($ocLazyLoad){
                return $ocLazyLoad.load(
                {
                    name:'sbAdminApp',
                    files:[
                    'scripts/directives/header/header.js',
                    'scripts/directives/header/header-notification/header-notification.js',
                    'scripts/directives/sidebar/sidebar.js',
                    'scripts/directives/sidebar/sidebar-search/sidebar-search.js'
                    ]
                }),
                $ocLazyLoad.load(
                {
                   name:'toggle-switch',
                   files:["bower_components/angular-toggle-switch/angular-toggle-switch.min.js",
                          "bower_components/angular-toggle-switch/angular-toggle-switch.css"
                      ]
                }),
                $ocLazyLoad.load(
                {
                  name:'ngAnimate',
                  files:['bower_components/angular-animate/angular-animate.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngCookies',
                  files:['bower_components/angular-cookies/angular-cookies.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngResource',
                  files:['bower_components/angular-resource/angular-resource.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngSanitize',
                  files:['bower_components/angular-sanitize/angular-sanitize.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngTouch',
                  files:['bower_components/angular-touch/angular-touch.js']
                })
                $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
            }
        }
    })
      .state('dashboard.home',{
        url:'/home',
        controller: 'MainCtrl',
        templateUrl:'views/dashboard/home.html',
        resolve: {
          loadMyFiles:function($ocLazyLoad) {
            return $ocLazyLoad.load({
				 name:'chart.js',
              files:[
                'bower_components/angular-chart.js/dist/angular-chart.min.js',
                'bower_components/angular-chart.js/dist/angular-chart.css'
              ]
            }),
			 $ocLazyLoad.load({
              name:'sbAdminApp',
              files:[
              'scripts/controllers/main.js',
              'scripts/directives/timeline/timeline.js',
              'scripts/directives/notifications/notifications.js',
              'scripts/directives/chat/chat.js',
              'scripts/directives/dashboard/stats/stats.js',
			  'scripts/controllers/chartContoller.js',
              'scripts/controllers/table.js'
              ]
            })
          }
        }
      })
      .state('dashboard.form',{
        templateUrl:'views/form.html',
        url:'/form'
    })
      .state('dashboard.blank',{
        templateUrl:'views/pages/blank.html',
        url:'/blank'
    })
      .state('login',{
        templateUrl:'views/pages/login.html',
        url:'/login'
    })
      .state('dashboard.chart',{
        templateUrl:'views/chart.html',
        url:'/chart',
        controller:'ChartCtrl',
        resolve: {
          loadMyFile:function($ocLazyLoad) {
            return $ocLazyLoad.load({
              name:'chart.js',
              files:[
                'bower_components/angular-chart.js/dist/angular-chart.min.js',
                'bower_components/angular-chart.js/dist/angular-chart.css'
              ]
            }),
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:['scripts/controllers/chartContoller.js']
            })
          }
        }
    })
      .state('dashboard.table',{
        templateUrl:'views/table.html',
        url:'/table'
    })
      .state('dashboard.panels-wells',{
          templateUrl:'views/ui-elements/panels-wells.html',
          url:'/panels-wells'
      })
      .state('dashboard.buttons',{
        templateUrl:'views/ui-elements/buttons.html',
        url:'/buttons'
    })
      .state('dashboard.notifications',{
        templateUrl:'views/ui-elements/notifications.html',
        url:'/notifications'
    })
      .state('dashboard.typography',{
       templateUrl:'views/ui-elements/typography.html',
       url:'/typography'
   })
      .state('dashboard.icons',{
       templateUrl:'views/ui-elements/icons.html',
       url:'/icons'
   })
      .state('dashboard.grid',{
       templateUrl:'views/ui-elements/grid.html',
       url:'/grid'
   })
	.state('dashboard.sensorListTable',{
       templateUrl:'views/ui-elements/sensorListTable.html',
       url:'/sensorListTable',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }

   })
    .state('dashboard.customizeCloud',{
       templateUrl:'views/ui-elements/customizeCloud.html',
       url:'/customizeCloud',
       controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            }
        }
   })
    .state('dashboard.actuatorListTable',{
       templateUrl:'views/ui-elements/actuatorListTable.html',
       url:'/actuatorListTable',
       controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })
     .state('dashboard.triggerListTable',{
       templateUrl:'views/ui-elements/triggerListTable.html',
       url:'/triggerListTable',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js']
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })
     .state('dashboard.addTrigger',{
       templateUrl:'views/ui-elements/addTrigger.html',
       url:'/addTrigger',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js'
				]
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })
     .state('dashboard.deleteTrigger',{
       templateUrl:'views/ui-elements/deleteTrigger.html',
       url:'/deleteTrigger',
       controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js'
				]
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })

   .state('dashboard.errorListTable',{
       templateUrl:'views/ui-elements/errorListTable.html',
       url:'/errorListTable',
        controller:'MainCtrl',
       resolve: {
          loadMyFile:function($ocLazyLoad) {
            $ocLazyLoad.load({
                name:'sbAdminApp',
                files:[
				'scripts/controllers/main.js',
				'scripts/controllers/table.js'
				]
            })
            $ocLazyLoad.load(
                {
                  name:'ngRoute',
                  files:['bower_components/angular-route/angular-route.js']
                })
          }
        }
   })

  }]);
<|endoftext|>


<commit_before>/**
 * @class CM_Form_Abstract
 * @extends CM_View_Abstract
 */
var CM_Form_Abstract = CM_View_Abstract.extend({
  _class: 'CM_Form_Abstract',

  /** @type Object **/
  _fields: {},

  /** @type Object **/
  _actions: {},

  /** @type Boolean **/
  _stopErrorPropagation: false,

  initialize: function() {
    CM_View_Abstract.prototype.initialize.call(this);

    this._fields = {};
    this._actions = {};
  },

  events: {
    'reset': function() {
      _.each(this._fields, function(field) {
        field.reset();
      });
      this.resetErrors();
    }
  },

  ready: function() {
  },

  _ready: function() {
    var handler = this;

    _.each(this._actions, function(action, name) {
      var $btn = $('#' + this.getAutoId() + '-' + name + '-button');
      var event = $btn.data('event');
      if (!event) {
        event = 'click';
      }
      $btn.on(event, {action: name}, function(event) {
        event.preventDefault();
        event.stopPropagation();
        return handler.submit(event.data.action);
      });
    }, this);

    this.$el.on('submit', function() {
      handler.$el.find('input[type="submit"], button[type="submit"]').first().click();
      return false;
    });

    CM_View_Abstract.prototype._ready.call(this);
  },

  /**
   * @param {CM_FormField_Abstract} field
   */
  registerField: function(field) {
    this._fields[field.getName()] = field;

    field.on('change', function() {
      this.trigger('change');
    }, this);
  },

  /**
   * @param {String} name
   * @param {Object} presentation
   */
  registerAction: function(name, presentation) {
    this._actions[name] = presentation;
  },

  /**
   * @return CM_FormField_Abstract
   */
  getField: function(name) {
    if (!this._fields[name]) {
      cm.error.triggerThrow(this.getClass() + ' cannot find form field `' + name + '`');
    }
    return this._fields[name];
  },

  /**
   * @return Boolean
   */
  hasField: function(name) {
    return !!this._fields[name];
  },

  /**
   * @return jQuery
   */
  $: function(selector) {
    if (!selector) {
      return this.$el;
    }
    selector = selector.replace('#', '#' + this.getAutoId() + '-');
    return $(selector, this.el);
  },

  /**
   * @param {String|Null} [actionName]
   */
  getData: function(actionName) {
    var form_data = this.$().serializeArray();
    var action = actionName ? this._actions[actionName] : null;

    var data = {};
    var regex = /^([\w\-]+)(\[([^\]]+)?\])?$/;
    var name, match;

    for (var i = 0, item; item = form_data[i]; i++) {
      match = regex.exec(item.name);
      name = match[1];
      item.value = item.value || '';

      if (action && typeof action.fields[name] == 'undefined') {
        continue;
      }

      if (!match[2]) {
        // Scalar
        data[name] = item.value;
      } else if (match[2] == '[]') {
        // Array
        if (typeof data[name] == 'undefined') {
          data[name] = [];
        }
        data[name].push(item.value);
      } else if (match[3]) {
        // Associative array
        if (typeof data[name] == 'undefined') {
          data[name] = {};
        }
        data[name][match[3]] = item.value;
      }
    }

    return data;
  },

  /**
   * @param {String} [actionName]
   * @param {Object} [options]
   * @return Promise
   */
  submit: function(actionName, options) {
    options = _.defaults(options || {}, {
      handleErrors: true,
      disableUI: true
    });
    var action = this._getAction(actionName);
    var data = this.getData(action.name);
    var errorList = this._getErrorList(action.name);

    if (options.handleErrors) {
      _.each(this._fields, function(field, fieldName) {
        if (errorList[fieldName]) {
          field.error(errorList[fieldName]);
        } else {
          field.error(null);
        }
      }, this);
    }

    if (_.size(errorList)) {
      return Promise.resolve();//TODO is this replacement ok. It is not rejected but resolved with empty value.
    } else {
      if (options.disableUI) {
        this.disable();
      }
      this.trigger('submit', [data]);

      var handler = this;
      return cm.ajax('form', {viewInfoList: this.getViewInfoList(), actionName: action.name, data: data})
        .then(function(response) {
          if (response.errors) {
            if (options.handleErrors) {
              for (var i = response.errors.length - 1, error; error = response.errors[i]; i--) {
                if (_.isArray(error)) {
                  handler.getField(error[1]).error(error[0]);
                } else {
                  handler.error(error);
                }
              }
            }

            handler.trigger('error error.' + action.name);
            return;
          }

          if (response.exec) {
            handler.evaluation = new Function(response.exec);
            handler.evaluation();
          }

          if (response.messages) {
            for (var i = 0, msg; msg = response.messages[i]; i++) {
              handler.message(msg);
            }
          }

          handler.trigger('success success.' + action.name, response.data);
          return response.data;
        })
        .catch(function(error){
          handler._stopErrorPropagation = false;
          handler.trigger('error error.' + action.name, error.msg, error.type, error.isPublic);
          throw error;
        })
        .finally(function(){
          if (options.disableUI) {
            handler.enable();
          }
          handler.trigger('complete');
        });
    }
  },

  stopErrorPropagation: function() {
    this._stopErrorPropagation = true;
  },

  reset: function() {
    this.el.reset();
  },

  disable: function() {
    this.$().disable();
  },

  enable: function() {
    this.$().enable();
  },

  /**
   * @param {String} message
   */
  error: function(message) {
    cm.window.hint(message);
  },

  /**
   * @param {String} message
   */
  message: function(message) {
    cm.window.hint(message);
  },

  resetErrors: function() {
    _.each(this._fields, function(field) {
      field.error(null);
    });
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getAction: function(actionName) {
    actionName = actionName || _.first(_.keys(this._actions));
    var action = this._actions[actionName];
    if (!action) {
      cm.error.triggerThrow('Form `' + this.getClass() + '` has no action `' + actionName + '`.');
    }
    action.name = actionName;
    return action;
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getErrorList: function(actionName) {
    var action = this._getAction(actionName);
    var data = this.getData(action.name);

    var errorList = {};

    _.each(_.keys(action.fields).reverse(), function(fieldName) {
      var required = action.fields[fieldName];
      if (required) {
        var field = this.getField(fieldName);
        if (field.isEmpty(data[fieldName])) {
          var label;
          var $textInput = field.$('input, textarea');
          var $labels = $('label[for="' + field.getAutoId() + '-input"]');
          if ($labels.length) {
            label = $labels.first().text();
          } else if ($textInput.attr('placeholder')) {
            label = $textInput.attr('placeholder');
          }
          if (label) {
            errorList[fieldName] = cm.language.get('{$label} is required.', {label: label});
          } else {
            errorList[fieldName] = cm.language.get('Required');
          }
        }
      }
    }, this);

    return errorList;
  }
});
<commit_msg>Streamline Ajax promise.<commit_after>/**
 * @class CM_Form_Abstract
 * @extends CM_View_Abstract
 */
var CM_Form_Abstract = CM_View_Abstract.extend({
  _class: 'CM_Form_Abstract',

  /** @type Object **/
  _fields: {},

  /** @type Object **/
  _actions: {},

  /** @type Boolean **/
  _stopErrorPropagation: false,

  initialize: function() {
    CM_View_Abstract.prototype.initialize.call(this);

    this._fields = {};
    this._actions = {};
  },

  events: {
    'reset': function() {
      _.each(this._fields, function(field) {
        field.reset();
      });
      this.resetErrors();
    }
  },

  ready: function() {
  },

  _ready: function() {
    var handler = this;

    _.each(this._actions, function(action, name) {
      var $btn = $('#' + this.getAutoId() + '-' + name + '-button');
      var event = $btn.data('event');
      if (!event) {
        event = 'click';
      }
      $btn.on(event, {action: name}, function(event) {
        event.preventDefault();
        event.stopPropagation();
        return handler.submit(event.data.action);
      });
    }, this);

    this.$el.on('submit', function() {
      handler.$el.find('input[type="submit"], button[type="submit"]').first().click();
      return false;
    });

    CM_View_Abstract.prototype._ready.call(this);
  },

  /**
   * @param {CM_FormField_Abstract} field
   */
  registerField: function(field) {
    this._fields[field.getName()] = field;

    field.on('change', function() {
      this.trigger('change');
    }, this);
  },

  /**
   * @param {String} name
   * @param {Object} presentation
   */
  registerAction: function(name, presentation) {
    this._actions[name] = presentation;
  },

  /**
   * @return CM_FormField_Abstract
   */
  getField: function(name) {
    if (!this._fields[name]) {
      cm.error.triggerThrow(this.getClass() + ' cannot find form field `' + name + '`');
    }
    return this._fields[name];
  },

  /**
   * @return Boolean
   */
  hasField: function(name) {
    return !!this._fields[name];
  },

  /**
   * @return jQuery
   */
  $: function(selector) {
    if (!selector) {
      return this.$el;
    }
    selector = selector.replace('#', '#' + this.getAutoId() + '-');
    return $(selector, this.el);
  },

  /**
   * @param {String|Null} [actionName]
   */
  getData: function(actionName) {
    var form_data = this.$().serializeArray();
    var action = actionName ? this._actions[actionName] : null;

    var data = {};
    var regex = /^([\w\-]+)(\[([^\]]+)?\])?$/;
    var name, match;

    for (var i = 0, item; item = form_data[i]; i++) {
      match = regex.exec(item.name);
      name = match[1];
      item.value = item.value || '';

      if (action && typeof action.fields[name] == 'undefined') {
        continue;
      }

      if (!match[2]) {
        // Scalar
        data[name] = item.value;
      } else if (match[2] == '[]') {
        // Array
        if (typeof data[name] == 'undefined') {
          data[name] = [];
        }
        data[name].push(item.value);
      } else if (match[3]) {
        // Associative array
        if (typeof data[name] == 'undefined') {
          data[name] = {};
        }
        data[name][match[3]] = item.value;
      }
    }

    return data;
  },

  /**
   * @param {String} [actionName]
   * @param {Object} [options]
   * @return Promise
   */
  submit: function(actionName, options) {
    options = _.defaults(options || {}, {
      handleErrors: true,
      disableUI: true
    });
    var action = this._getAction(actionName);
    var data = this.getData(action.name);
    var errorList = this._getErrorList(action.name);

    if (options.handleErrors) {
      _.each(this._fields, function(field, fieldName) {
        if (errorList[fieldName]) {
          field.error(errorList[fieldName]);
        } else {
          field.error(null);
        }
      }, this);
    }

    if (_.size(errorList)) {
      return Promise.resolve();
    } else {
      if (options.disableUI) {
        this.disable();
      }
      this.trigger('submit', [data]);

      var handler = this;
      return cm.ajax('form', {viewInfoList: this.getViewInfoList(), actionName: action.name, data: data})
        .then(function(response) {
          if (response.errors) {
            if (options.handleErrors) {
              for (var i = response.errors.length - 1, error; error = response.errors[i]; i--) {
                if (_.isArray(error)) {
                  handler.getField(error[1]).error(error[0]);
                } else {
                  handler.error(error);
                }
              }
            }

            handler.trigger('error error.' + action.name);
            return;
          }

          if (response.exec) {
            handler.evaluation = new Function(response.exec);
            handler.evaluation();
          }

          if (response.messages) {
            for (var i = 0, msg; msg = response.messages[i]; i++) {
              handler.message(msg);
            }
          }

          handler.trigger('success success.' + action.name, response.data);
          return response.data;
        })
        .catch(function(error){
          handler._stopErrorPropagation = false;
          handler.trigger('error error.' + action.name, error.msg, error.type, error.isPublic);
          throw error;
        })
        .finally(function(){
          if (options.disableUI) {
            handler.enable();
          }
          handler.trigger('complete');
        });
    }
  },

  stopErrorPropagation: function() {
    this._stopErrorPropagation = true;
  },

  reset: function() {
    this.el.reset();
  },

  disable: function() {
    this.$().disable();
  },

  enable: function() {
    this.$().enable();
  },

  /**
   * @param {String} message
   */
  error: function(message) {
    cm.window.hint(message);
  },

  /**
   * @param {String} message
   */
  message: function(message) {
    cm.window.hint(message);
  },

  resetErrors: function() {
    _.each(this._fields, function(field) {
      field.error(null);
    });
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getAction: function(actionName) {
    actionName = actionName || _.first(_.keys(this._actions));
    var action = this._actions[actionName];
    if (!action) {
      cm.error.triggerThrow('Form `' + this.getClass() + '` has no action `' + actionName + '`.');
    }
    action.name = actionName;
    return action;
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getErrorList: function(actionName) {
    var action = this._getAction(actionName);
    var data = this.getData(action.name);

    var errorList = {};

    _.each(_.keys(action.fields).reverse(), function(fieldName) {
      var required = action.fields[fieldName];
      if (required) {
        var field = this.getField(fieldName);
        if (field.isEmpty(data[fieldName])) {
          var label;
          var $textInput = field.$('input, textarea');
          var $labels = $('label[for="' + field.getAutoId() + '-input"]');
          if ($labels.length) {
            label = $labels.first().text();
          } else if ($textInput.attr('placeholder')) {
            label = $textInput.attr('placeholder');
          }
          if (label) {
            errorList[fieldName] = cm.language.get('{$label} is required.', {label: label});
          } else {
            errorList[fieldName] = cm.language.get('Required');
          }
        }
      }
    }, this);

    return errorList;
  }
});
<|endoftext|>


<commit_before>/**
 * @class  	Router
 * @author  Flavio De Stefano <flavio.destefano@caffeinalab.com>
 */

/**
 * @property config
 * @property {String} config.protocol  Force all protocol different from this to be discarded
 */
exports.config = _.extend({
	protocol: null
}, Alloy.CFG.T ? Alloy.CFG.T.router : {});

var Util = require('T/util');
var Flow = require('T/flow');
var Q = require('T/ext/q');

var routeRegistry = [];

/**
 * @property currentUrl
 * Latest URL dispatched
 * @type {String}
 */
exports.currentUrl = null;

/**
 * @property currentRoute
 * Latest Route (not URL) dispatched
 * @type {Object}
 */
exports.currentRoute = null;

/**
 * @property stack
 * All routes in a stack
 * @type {Array}
 */
exports.stack = [];

/**
 * @method on
 * Register a route with defined callbacks
 *
 * @param {Object} key The route name.
 * It can be:
 *
 * * `String`: (exact route match)
 * * `RegExp`: is evaluated with the argument and the matches are passed to the callback
 * * `Function`: must return a `non-undefined` value to be executed. That value is passed to the callback
 *
 * @param  {Function}	callback  		The callback
 */
exports.on = function() {
	var callbacks = _.toArray(arguments);
	var key = callbacks.shift();
	var callback = callbacks.pop();

	routeRegistry.push({
		key: key,
		callback: callback,
		callbacks: callbacks
	});
};

/**
 * @method dispatch
 * Dispatch the router
 *
 * This function call the defined function with `Router.on`
 *
 * The param `this` represents an XCallback-URL Object for selected route.
 *
 * See https://github.com/FokkeZB/UTiL/blob/master/XCallbackURL/XCallbackURL.js for more details.
 *
 * The arguments passed are the matches for your regex definition (if present)
 *
 * @param  {String} url 		The route
 */
exports.dispatch = function(url) {
	Ti.API.debug('Router: dispatching <' + url + '>');

	var callbackURL = Util.parseAsXCallbackURL(url);
	callbackURL.path = callbackURL.path.replace(/\/$/g, '');

	if (callbackURL.protocol && exports.config.protocol) {
		if (exports.config.protocol !== callbackURL.protocol) {
			Ti.API.warn('Router: protocol mismatch');
			return false;
		}
	}

	var run = false;
	var matches = null;

	// Check the route to dispatch
	for (var i in routeRegistry) {
		var routeDefinition = routeRegistry[i];

		if (_.isString(routeDefinition.key)) {

			///////////////////////////
			// Regular string equals //
			///////////////////////////

			run = (routeDefinition.key === callbackURL.path);

		} else if (_.isRegExp(routeDefinition.key)) {

			//////////////////////////////////////
			// Regular expression complex match //
			//////////////////////////////////////

			matches = callbackURL.path.match(routeDefinition.key);
			run = !!(matches);
			if (matches) matches.shift();

		} else if (_.isFunction(routeDefinition.key)) {

			////////////////////
			// Function match //
			////////////////////

			matches = routeDefinition.key(callbackURL.path);
			run = (matches !== undefined);

		}

		////////////
		// Run if //
		////////////

		if (run === true) {
			Ti.API.debug('Router: matched on <' + routeDefinition.key + ', ' + JSON.stringify(matches) + '>');

			if (_.isFunction(routeDefinition.callback)) {

				exports.stack.push(url);
				exports.currentUrl = url;
				exports.currentRoute = routeDefinition;

				console.log( _.isArray(routeDefinition.callbacks), routeDefinition.callbacks.toString() );

				if (routeDefinition.callbacks.length > 0) {
					routeDefinition.callbacks.reduce(Q.when, Q()).then(function() {
						routeDefinition.callback.apply(callbackURL, matches);
					});
				} else {
					routeDefinition.callback.apply(callbackURL, matches);
				}

			} else if (_.isObject(routeDefinition.callback)) {

				if (routeDefinition.callback.alias != null) {
					exports.dispatch(routeDefinition.callback.alias);
				}

			}

			return true;
		}
	}

	Ti.API.warn('Router: no match for <' + url + '>');
	return false;
};

/**
 * @method go
 * @inheritDoc #dispatch
 * Alias for {@link #dispatch}
 */
exports.go = exports.dispatch;

/**
 * Make an alias route
 * @param  {String} url
 * @param  {String} newUrl
 */
exports.alias = function(url, newUrl) {
	exports.on(url, {
		alias: newUrl
	});
};

/**
 * @method autoMapModel
 * Create the routes for a model
 * @param  {String} single The name for the model
 * @param  {String} [plural] The name for the model, plural.
 */
exports.autoMapModel = function(single, plural) {
	plural = plural || single+'s';

	exports.on('/' + plural, function() {
		Flow.open(plural, {}, {}, this.source);
	});

	exports.on(new RegExp('/' + plural + '/([0-9]+)'), function(id) {
		Flow.open(single, {
			id: id
		}, {}, this.source);
	});
};
<commit_msg>Removed log<commit_after>/**
 * @class  	Router
 * @author  Flavio De Stefano <flavio.destefano@caffeinalab.com>
 */

/**
 * @property config
 * @property {String} config.protocol  Force all protocol different from this to be discarded
 */
exports.config = _.extend({
	protocol: null
}, Alloy.CFG.T ? Alloy.CFG.T.router : {});

var Util = require('T/util');
var Flow = require('T/flow');
var Q = require('T/ext/q');

var routeRegistry = [];

/**
 * @property currentUrl
 * Latest URL dispatched
 * @type {String}
 */
exports.currentUrl = null;

/**
 * @property currentRoute
 * Latest Route (not URL) dispatched
 * @type {Object}
 */
exports.currentRoute = null;

/**
 * @property stack
 * All routes in a stack
 * @type {Array}
 */
exports.stack = [];

/**
 * @method on
 * Register a route with defined callbacks
 *
 * @param {Object} key The route name.
 * It can be:
 *
 * * `String`: (exact route match)
 * * `RegExp`: is evaluated with the argument and the matches are passed to the callback
 * * `Function`: must return a `non-undefined` value to be executed. That value is passed to the callback
 *
 * @param  {Function}	callback  		The callback
 */
exports.on = function() {
	var callbacks = _.toArray(arguments);
	var key = callbacks.shift();
	var callback = callbacks.pop();

	routeRegistry.push({
		key: key,
		callback: callback,
		callbacks: callbacks,
		defer: defer
	});
};

/**
 * @method dispatch
 * Dispatch the router
 *
 * This function call the defined function with `Router.on`
 *
 * The param `this` represents an XCallback-URL Object for selected route.
 *
 * See https://github.com/FokkeZB/UTiL/blob/master/XCallbackURL/XCallbackURL.js for more details.
 *
 * The arguments passed are the matches for your regex definition (if present)
 *
 * @param  {String} url 		The route
 */
exports.dispatch = function(url) {
	Ti.API.debug('Router: dispatching <' + url + '>');

	var callbackURL = Util.parseAsXCallbackURL(url);
	callbackURL.path = callbackURL.path.replace(/\/$/g, '');

	if (callbackURL.protocol && exports.config.protocol) {
		if (exports.config.protocol !== callbackURL.protocol) {
			Ti.API.warn('Router: protocol mismatch');
			return false;
		}
	}

	var run = false;
	var matches = null;

	// Check the route to dispatch
	for (var i in routeRegistry) {
		var routeDefinition = routeRegistry[i];

		if (_.isString(routeDefinition.key)) {

			///////////////////////////
			// Regular string equals //
			///////////////////////////

			run = (routeDefinition.key === callbackURL.path);

		} else if (_.isRegExp(routeDefinition.key)) {

			//////////////////////////////////////
			// Regular expression complex match //
			//////////////////////////////////////

			matches = callbackURL.path.match(routeDefinition.key);
			run = !!(matches);
			if (matches) matches.shift();

		} else if (_.isFunction(routeDefinition.key)) {

			////////////////////
			// Function match //
			////////////////////

			matches = routeDefinition.key(callbackURL.path);
			run = (matches !== undefined);

		}

		////////////
		// Run if //
		////////////

		if (run === true) {
			Ti.API.debug('Router: matched on <' + routeDefinition.key + ', ' + JSON.stringify(matches) + '>');

			if (_.isFunction(routeDefinition.callback)) {

				exports.stack.push(url);
				exports.currentUrl = url;
				exports.currentRoute = routeDefinition;

				if (routeDefinition.callbacks.length > 0) {
					routeDefinition.callbacks.reduce(Q.when, Q()).then(function() {
						routeDefinition.callback.apply(callbackURL, matches);
					});
				} else {
					routeDefinition.callback.apply(callbackURL, matches);
				}

			} else if (_.isObject(routeDefinition.callback)) {

				if (routeDefinition.callback.alias != null) {
					exports.dispatch(routeDefinition.callback.alias);
				}

			}

			return true;
		}
	}

	Ti.API.warn('Router: no match for <' + url + '>');
	return false;
};

/**
 * @method go
 * @inheritDoc #dispatch
 * Alias for {@link #dispatch}
 */
exports.go = exports.dispatch;

/**
 * Make an alias route
 * @param  {String} url
 * @param  {String} newUrl
 */
exports.alias = function(url, newUrl) {
	exports.on(url, {
		alias: newUrl
	});
};

/**
 * @method autoMapModel
 * Create the routes for a model
 * @param  {String} single The name for the model
 * @param  {String} [plural] The name for the model, plural.
 */
exports.autoMapModel = function(single, plural) {
	plural = plural || single+'s';

	exports.on('/' + plural, function() {
		Flow.open(plural, {}, {}, this.source);
	});

	exports.on(new RegExp('/' + plural + '/([0-9]+)'), function(id) {
		Flow.open(single, {
			id: id
		}, {}, this.source);
	});
};
<|endoftext|>


<commit_before>function MyuwLog()  {
    var myuwlog = this;

    this.link_logger = undefined;
    this.card_logger = undefined;
    this.appender = undefined;

    this.init = function() {
        this.link_logger = this.get_logger('link');
        this.card_logger = this.get_logger('card');

        $(window).unload(function() {
            window.myuw_log.send_links();
        });
    };

    this.log_card = function(card, action, extra_args) {
        var message = "",
            key;
        if (typeof(card.element)=== "object"){
            extra_args = extra_args || {};
            message = {card_name: LogUtils.get_card_name(card),
                       card_info: $(card.element).attr('data-identifier'),
                       card_position: card.pos,
                       action: action};

            for (key in extra_args) {
                if (extra_args.hasOwnProperty(key)) {
                    message[key] = extra_args[key];
                }
            }
        }else if (typeof(card) === "string"){
            message = {card_name: card,
                       action: action};
        }

        this.card_logger.info(JSON.stringify(message));
    };
    this.log_link = function(link, action) {
        var parent_cards = $(link).closest('.card');
        var card_name;
        var card_info;
        if (parent_cards.length > 0){
            card_info = $(parent_cards[0]).attr('data-identifier');
            card_name = $(parent_cards[0]).attr('data-name');
        }

        var href = $(link).attr('href');
        if (href !== "#") {
            var message = {href: $(link).attr('href'),
                           action: action,
                           source_card: card_name,
                           card_info: card_info};
            if (href.indexOf("notices") > -1) {
                message.unread_notice_count = Notices.get_total_unread();
            }
            this.link_logger.info(JSON.stringify(message));
        }
    };
    this.send_links = function() {
        this.appender.sendAllRemaining();
    };

    this.get_logger = function(name) {
        if (this.appender === undefined) {
            this.appender = this.get_appender();
        }
        var log = log4javascript.getLogger(name);
        log.addAppender(this.appender);

        //ensure all error messages are suppressed
        log4javascript.logLog.setQuietMode(true);

        //ensure logs are sent if a user doesn't navigate away
        window.setInterval(function() {
            window.myuw_log.send_links();
        }, 5000);
        return log;
    };

    this.get_appender = function() {
        var json_layout = new log4javascript.JsonLayout(true);
        var append;
        if (window.location.hash == "#console") {
            append = new log4javascript.BrowserConsoleAppender('/logging/log');
        }
        else {
            append = new log4javascript.AjaxAppender('/logging/log');
            append.setBatchSize(20);
            append.addHeader("X-CSRFToken", csrf_token);
        }
        append.setLayout(json_layout);
        return append;
    };
}

var LogUtils = {
    PERCENTAGE_VISIBLE_THRESHOLD: 0.80,
    current_visible_cards: {},

    get_new_visible_cards: function(){
        var cards = LogUtils.get_all_cards(),
            card_id,
            visible_cards = [];

        $(cards).each(function(i, card){
            if(LogUtils.isScrolledIntoView(card.element)){
                visible_cards.push(card);
            }

        });

        var log_values = LogUtils.evaluateCurrentlyVisibleCards(visible_cards);
        LogUtils.logCardOnscreenChanges(log_values);
    },

    isScrolledIntoView: function(elem) {
       var docViewTop = $(window).scrollTop();
       var docViewBottom = docViewTop + $(window).height();

       var elmHeight = $(elem).height();
       var elemTop = $(elem).offset().top;
       var elemBottom = elemTop + elmHeight;

       return LogUtils.isInViewport(docViewTop, docViewBottom, elemTop, elemBottom);
    },

    isInViewport: function(viewport_top, viewport_bottom, elem_top, elem_bottom) {
        // 2 cases of completely off-screen
        if (elem_bottom < viewport_top) {
            return false;
        }
        if (elem_top > viewport_bottom) {
            return false;
        }

        // If it's fully on-screen...
        if ((elem_bottom <= viewport_bottom) && (elem_top >= viewport_top)) {
            return true;
        }

        var visible_height;
        // If we're above the viewport:
        if (elem_top < viewport_top) {
            visible_height = elem_bottom - viewport_top;
        }
        else {
            visible_height = viewport_bottom - elem_top;
        }
        var actual_height = elem_bottom - elem_top;
        var viewport_size = viewport_bottom - viewport_top;

        // If the top or bottom of the element is off-screen...
        // it can be on-screen if 80% of the card is visible
        if (visible_height >= actual_height * LogUtils.PERCENTAGE_VISIBLE_THRESHOLD) {
            return true;
        }

        // or if the card takes up 80% of the screen
        if (visible_height >= viewport_size * LogUtils.PERCENTAGE_VISIBLE_THRESHOLD) {
            return true;
        }

        return false;
    },

    get_new_visible_links: function () {
        var links = LogUtils.get_links_in_view();
        $(links).each(function(i, link) {
            var href = $(link).attr('href');
            if(!window.viewed_links.hasOwnProperty(href)){
                window.viewed_links[href] = link;
                window.myuw_log.log_link(link, "view");
            }

        });
    },

    get_links_in_view: function(){
       var links = [];
       $("a").each(function (i, link_elm) {
           var href = $(link_elm).attr('href');
           if (href !== "#"){
               if(LogUtils.isScrolledIntoView(link_elm)){
                   //Ensure link or parents aren't hidden
                   if ($(link_elm).attr("aria-hidden") !== true &&
                           $(link_elm).parents('*[aria-hidden="true"]').length === 0){
                       links.push(link_elm);
                   }
               }
           }
       });
       return links;
    },

    get_all_cards: function(){
       var cards = [],
           pos = 0;
       $("div").find("[data-type='card']").each(function (i, card) {
           pos++;
           cards.push({element: card, pos: pos});
       });
       return cards;
    },

    _init_link_logging: function() {
       $(document).on("click", "a", function () {
           window.myuw_log.log_link(this, "click");
           window.myuw_log.send_links();
       });
       LogUtils.de_bouncer(jQuery,'smartscroll', 'scroll', 100);
       window.viewed_links = {};
       $(window).smartscroll(function(e) {
           LogUtils.get_new_visible_links();
       });
    },

    _init_card_logging: function() {
        LogUtils.registerCardLoadEvents();
        LogUtils.de_bouncer(jQuery,'smartscroll', 'scroll', 100);
        $(window).smartscroll(function(e) {
            LogUtils.get_new_visible_cards();
        });
    },

    de_bouncer: function($,cf,of, interval){
       var debounce = function (func, threshold, execAsap) {
           var timeout;
           return function debounced () {
               var obj = this, args = arguments;
               function delayed () {
                   if (!execAsap)
                       func.apply(obj, args);
                   timeout = null;
               }
               if (timeout)
                   clearTimeout(timeout);
               else if (execAsap)
                   func.apply(obj, args);
               timeout = setTimeout(delayed, threshold || interval);
           };
       };
       jQuery.fn[cf] = function(fn){  return fn ? this.bind(of, debounce(fn)) : this.trigger(cf); };
    },

    init_logging: function () {
       myuwlog = new MyuwLog();

       myuwlog.init();
       window.myuw_log = myuwlog;
       LogUtils._init_link_logging();
       LogUtils._init_card_logging();
    },

    get_card_name: function(card) {
        return $(card.element).attr('data-name');
    },

    evaluateCurrentlyVisibleCards: function(cards) {
        var values = {
            scrolled_out: [],
            scrolled_in: []
        };

        var new_visible_cards = {};
        for (var i = 0, len = cards.length; i < len; i++) {
            var card = cards[i];
            var card_name = LogUtils.get_card_name(card);

            if (LogUtils.current_visible_cards[card_name]) {
                new_visible_cards[card_name] = LogUtils.current_visible_cards[card_name];
                delete LogUtils.current_visible_cards[card_name];
            }
            else {
                new_visible_cards[card_name] = {
                    card: card,
                    first_onscreen: new Date().getTime(),
                    is_newly_read: false,
                    has_been_read: false,
                    rand: Math.random().toString(36).substring(2)
                };

                values.scrolled_in.push(new_visible_cards[card_name]);
            }
        }
        var key;
        for (key in LogUtils.current_visible_cards) {
            if (LogUtils.current_visible_cards.hasOwnProperty(key)) {
                if (!new_visible_cards[key]) {
                    values.scrolled_out.push(LogUtils.current_visible_cards[key]);
                    delete LogUtils.current_visible_cards[key];
                }
            }
        }

        LogUtils.current_visible_cards = new_visible_cards;
        return values;
    },

    logCardOnscreenChanges: function(changes) {
        var i,
            len,
            card;
        for (i = 0, len = changes.scrolled_in.length; i < len; i++) {
            window.myuw_log.log_card(changes.scrolled_in[i].card, "view", {
                rand: changes.scrolled_in[i].rand
            });
        }
        for (i = 0, len = changes.scrolled_out.length; i < len; i++) {
            card = changes.scrolled_out[i].card;
            window.myuw_log.log_card(card, "time_viewed", {
                time_visible: ((new Date().getTime()) - changes.scrolled_out[i].first_onscreen) / 1000,
                final: true,
                rand: changes.scrolled_out[i].rand
            });
        }
    },

    getCurrentlyVisibleCards: function() {
        var list = [],
            key;
        for (key in LogUtils.current_visible_cards) {
            if (LogUtils.current_visible_cards.hasOwnProperty(key)) {
                var card = LogUtils.current_visible_cards[key];
                card.time_visible = ((new Date().getTime()) - card.first_onscreen) / 1000;

                if (card.time_visible >= 1.0) {
                    if (!card.has_been_read) {
                        card.is_newly_read = true;
                    }
                    else {
                        card.is_newly_read = false;
                    }
                    card.has_been_read = true;
                }
                list.push(card);
            }
        }
        return list;
    },

    // This should only be used in the test harness.
    _resetVisibleCards: function() {
        LogUtils.current_visible_cards = {};
    },

    registerCardLoadEvents: function() {
        $(window).on("myuw:card_load", LogUtils._logCardLoaded);
    },

    _logCardLoaded: function(ev, name, el) {
        var message = {
            action: "loaded",
            card_name: name,
            on_screen: LogUtils.isScrolledIntoView(el)
        };
        window.myuw_log.card_logger.info(JSON.stringify(message));
        LogUtils.get_new_visible_cards();
        LogUtils.get_new_visible_links();
    },

    cardLoaded: function(name, el) {
        $(window).trigger("myuw:card_load", [ name, el ]);
    }
};

/* node.js exports */
if (typeof exports == "undefined") {
    var exports = {};
}
exports.LogUtils = LogUtils;
exports.MyuwLog = MyuwLog;

<commit_msg>Making it so there's only ever one interval set for our intervals.<commit_after>function MyuwLog()  {
    var myuwlog = this;

    this.link_logger = undefined;
    this.card_logger = undefined;
    this.appender = undefined;

    this.init = function() {
        this.link_logger = this.get_logger('link');
        this.card_logger = this.get_logger('card');

        $(window).unload(function() {
            window.myuw_log.send_links();
        });
    };

    this.log_card = function(card, action, extra_args) {
        var message = "",
            key;
        if (typeof(card.element)=== "object"){
            extra_args = extra_args || {};
            message = {card_name: LogUtils.get_card_name(card),
                       card_info: $(card.element).attr('data-identifier'),
                       card_position: card.pos,
                       action: action};

            for (key in extra_args) {
                if (extra_args.hasOwnProperty(key)) {
                    message[key] = extra_args[key];
                }
            }
        }else if (typeof(card) === "string"){
            message = {card_name: card,
                       action: action};
        }

        this.card_logger.info(JSON.stringify(message));
    };
    this.log_link = function(link, action) {
        var parent_cards = $(link).closest('.card');
        var card_name;
        var card_info;
        if (parent_cards.length > 0){
            card_info = $(parent_cards[0]).attr('data-identifier');
            card_name = $(parent_cards[0]).attr('data-name');
        }

        var href = $(link).attr('href');
        if (href !== "#") {
            var message = {href: $(link).attr('href'),
                           action: action,
                           source_card: card_name,
                           card_info: card_info};
            if (href.indexOf("notices") > -1) {
                message.unread_notice_count = Notices.get_total_unread();
            }
            this.link_logger.info(JSON.stringify(message));
        }
    };
    this.send_links = function() {
        this.appender.sendAllRemaining();
    };

    this.get_logger = function(name) {
        if (this.appender === undefined) {
            this.appender = this.get_appender();
        }
        var log = log4javascript.getLogger(name);
        log.addAppender(this.appender);

        //ensure all error messages are suppressed
        log4javascript.logLog.setQuietMode(true);

        //ensure logs are sent if a user doesn't navigate away
        if (!LogUtils.send_remaining_interval) {
            LogUtils.send_remaining_interval = window.setInterval(function() {
                window.myuw_log.send_links();
            }, LogUtils.PERIODIC_SEND_INTERVAL);
        }

        // Periodically log how long cards have been on screen
        if (!LogUtils.on_screen_interval_check) {
            LogUtils.on_screen_interval_check = window.setInterval(function() {
                LogUtils.get_new_visible_cards();
            }, LogUtils.PERIODIC_ONSCREEN_INTERVAL);
        }
        return log;
    };

    this.get_appender = function() {
        var json_layout = new log4javascript.JsonLayout(true);
        var append;
        if (window.location.hash == "#console") {
            append = new log4javascript.BrowserConsoleAppender('/logging/log');
        }
        else {
            append = new log4javascript.AjaxAppender('/logging/log');
            append.setBatchSize(20);
            append.addHeader("X-CSRFToken", csrf_token);
        }
        append.setLayout(json_layout);
        return append;
    };
}

var LogUtils = {
    PERCENTAGE_VISIBLE_THRESHOLD: 0.50,
    PERIODIC_ONSCREEN_INTERVAL: 1000,
    PERIODIC_SEND_INTERVAL: 2000,
    current_visible_cards: {},

    get_new_visible_cards: function(){
        var cards = LogUtils.get_all_cards(),
            card_id,
            visible_cards = [];

        $(cards).each(function(i, card){
            if(LogUtils.isScrolledIntoView(card.element)){
                visible_cards.push(card);
            }

        });

        var log_values = LogUtils.evaluateCurrentlyVisibleCards(visible_cards);
        LogUtils.logCardOnscreenChanges(log_values);
    },

    isScrolledIntoView: function(elem) {
       var docViewTop = $(window).scrollTop();
       var docViewBottom = docViewTop + $(window).height();

       var elmHeight = $(elem).height();
       var elemTop = $(elem).offset().top;
       var elemBottom = elemTop + elmHeight;

       return LogUtils.isInViewport(docViewTop, docViewBottom, elemTop, elemBottom);
    },

    isInViewport: function(viewport_top, viewport_bottom, elem_top, elem_bottom) {
        // 2 cases of completely off-screen
        if (elem_bottom < viewport_top) {
            return false;
        }
        if (elem_top > viewport_bottom) {
            return false;
        }

        // If it's fully on-screen...
        if ((elem_bottom <= viewport_bottom) && (elem_top >= viewport_top)) {
            return true;
        }

        var visible_height;
        // If we're above the viewport:
        if (elem_top < viewport_top) {
            visible_height = elem_bottom - viewport_top;
        }
        else {
            visible_height = viewport_bottom - elem_top;
        }
        var actual_height = elem_bottom - elem_top;
        var viewport_size = viewport_bottom - viewport_top;

        // If the top or bottom of the element is off-screen...
        // it can be on-screen if 80% of the card is visible
        if (visible_height >= actual_height * LogUtils.PERCENTAGE_VISIBLE_THRESHOLD) {
            return true;
        }

        // or if the card takes up 80% of the screen
        if (visible_height >= viewport_size * LogUtils.PERCENTAGE_VISIBLE_THRESHOLD) {
            return true;
        }

        return false;
    },

    get_new_visible_links: function () {
        var links = LogUtils.get_links_in_view();
        $(links).each(function(i, link) {
            var href = $(link).attr('href');
            if(!window.viewed_links.hasOwnProperty(href)){
                window.viewed_links[href] = link;
                window.myuw_log.log_link(link, "view");
            }

        });
    },

    get_links_in_view: function(){
       var links = [];
       $("a").each(function (i, link_elm) {
           var href = $(link_elm).attr('href');
           if (href !== "#"){
               if(LogUtils.isScrolledIntoView(link_elm)){
                   //Ensure link or parents aren't hidden
                   if ($(link_elm).attr("aria-hidden") !== true &&
                           $(link_elm).parents('*[aria-hidden="true"]').length === 0){
                       links.push(link_elm);
                   }
               }
           }
       });
       return links;
    },

    get_all_cards: function(){
       var cards = [],
           pos = 0;
       $("div").find("[data-type='card']").each(function (i, card) {
           pos++;
           cards.push({element: card, pos: pos});
       });
       return cards;
    },

    _init_link_logging: function() {
       $(document).on("click", "a", function () {
           window.myuw_log.log_link(this, "click");
           window.myuw_log.send_links();
       });
       LogUtils.de_bouncer(jQuery,'smartscroll', 'scroll', 100);
       window.viewed_links = {};
       $(window).smartscroll(function(e) {
           LogUtils.get_new_visible_links();
       });
    },

    _init_card_logging: function() {
        LogUtils.registerCardLoadEvents();
        LogUtils.de_bouncer(jQuery,'smartscroll', 'scroll', 100);
        $(window).smartscroll(function(e) {
            LogUtils.get_new_visible_cards();
        });
    },

    de_bouncer: function($,cf,of, interval){
       var debounce = function (func, threshold, execAsap) {
           var timeout;
           return function debounced () {
               var obj = this, args = arguments;
               function delayed () {
                   if (!execAsap)
                       func.apply(obj, args);
                   timeout = null;
               }
               if (timeout)
                   clearTimeout(timeout);
               else if (execAsap)
                   func.apply(obj, args);
               timeout = setTimeout(delayed, threshold || interval);
           };
       };
       jQuery.fn[cf] = function(fn){  return fn ? this.bind(of, debounce(fn)) : this.trigger(cf); };
    },

    init_logging: function () {
       myuwlog = new MyuwLog();

       myuwlog.init();
       window.myuw_log = myuwlog;
       LogUtils._init_link_logging();
       LogUtils._init_card_logging();
    },

    get_card_name: function(card) {
        return $(card.element).attr('data-name');
    },

    evaluateCurrentlyVisibleCards: function(cards) {
        var values = {
            scrolled_out: [],
            scrolled_in: []
        };

        var new_visible_cards = {};
        for (var i = 0, len = cards.length; i < len; i++) {
            var card = cards[i];
            var card_name = LogUtils.get_card_name(card);

            if (LogUtils.current_visible_cards[card_name]) {
                new_visible_cards[card_name] = LogUtils.current_visible_cards[card_name];
                delete LogUtils.current_visible_cards[card_name];
            }
            else {
                new_visible_cards[card_name] = {
                    card: card,
                    first_onscreen: new Date().getTime(),
                    is_newly_read: false,
                    has_been_read: false,
                    rand: Math.random().toString(36).substring(2)
                };

                values.scrolled_in.push(new_visible_cards[card_name]);
            }
        }
        var key;
        for (key in LogUtils.current_visible_cards) {
            if (LogUtils.current_visible_cards.hasOwnProperty(key)) {
                if (!new_visible_cards[key]) {
                    values.scrolled_out.push(LogUtils.current_visible_cards[key]);
                    delete LogUtils.current_visible_cards[key];
                }
            }
        }

        LogUtils.current_visible_cards = new_visible_cards;
        return values;
    },

    logCardOnscreenChanges: function(changes) {
        var i,
            len,
            card;
        for (i = 0, len = changes.scrolled_in.length; i < len; i++) {
            window.myuw_log.log_card(changes.scrolled_in[i].card, "view", {
                rand: changes.scrolled_in[i].rand
            });
        }
        for (i = 0, len = changes.scrolled_out.length; i < len; i++) {
            card = changes.scrolled_out[i].card;
            window.myuw_log.log_card(card, "time_viewed", {
                time_visible: ((new Date().getTime()) - changes.scrolled_out[i].first_onscreen) / 1000,
                final: true,
                rand: changes.scrolled_out[i].rand
            });
        }
    },

    getCurrentlyVisibleCards: function() {
        var list = [],
            key;
        for (key in LogUtils.current_visible_cards) {
            if (LogUtils.current_visible_cards.hasOwnProperty(key)) {
                var card = LogUtils.current_visible_cards[key];
                card.time_visible = ((new Date().getTime()) - card.first_onscreen) / 1000;

                if (card.time_visible >= 1.0) {
                    if (!card.has_been_read) {
                        card.is_newly_read = true;
                    }
                    else {
                        card.is_newly_read = false;
                    }
                    card.has_been_read = true;
                }
                list.push(card);
            }
        }
        return list;
    },

    // This should only be used in the test harness.
    _resetVisibleCards: function() {
        LogUtils.current_visible_cards = {};
    },

    registerCardLoadEvents: function() {
        $(window).on("myuw:card_load", LogUtils._logCardLoaded);
    },

    _logCardLoaded: function(ev, name, el) {
        var message = {
            action: "loaded",
            card_name: name,
            on_screen: LogUtils.isScrolledIntoView(el)
        };
        window.myuw_log.card_logger.info(JSON.stringify(message));
        LogUtils.get_new_visible_cards();
        LogUtils.get_new_visible_links();
    },

    cardLoaded: function(name, el) {
        $(window).trigger("myuw:card_load", [ name, el ]);
    }
};

/* node.js exports */
if (typeof exports == "undefined") {
    var exports = {};
}
exports.LogUtils = LogUtils;
exports.MyuwLog = MyuwLog;

<|endoftext|>