<commit_before>import watch from 'redux-watch';
import {RUNS_QUERY} from './graphql/runs';
import {matchPath} from 'react-router';
import {setFlash} from './actions';

let unsubscribe, runsChannel;
try {
  const p = require('Cloud/util/pusher');
  unsubscribe = p.unsubscribe;
  runsChannel = p.runsChannel;
} catch (e) {
  const p = require('./util/pusher');
  unsubscribe = p.unsubscribe;
  runsChannel = p.dummyChannel;
}

const SUBSCRIPTION_COOLDOWN = 20000;

function matchProject(path) {
  return matchPath(path, '/:entity/:project') || {params: {}};
}

function project(path) {
  return matchProject(path).params.project;
}

function updateRunsQuery(store, client, queryVars, payloads) {
  //console.time('updating runs');
  const state = store.getState();

  //Ensure we do the initial query
  const data = client.readQuery({
      query: RUNS_QUERY,
      variables: queryVars,
    }),
    edges = data.model.buckets.edges;

  for (var payload of payloads) {
    const sameUser = payload.user.username === state.global.user.entity;
    if (payload.state !== 'running' && sameUser) {
      //TODO: fix the finished hack
      //TODO: only dispatch if the run belongs to me
      //TODO: figure out why Lukas has his run marked as failed
      if (payload.state === 'failed') {
        console.log('failed run');
        //store.dispatch(setFlash({message: 'Run Failed', color: 'red'}));
      } else {
        store.dispatch(setFlash({message: 'Run Finished', color: 'green'}));
      }
    }

    let node;
    if (edges[0]) {
      node = Object.assign({}, edges[0].node, payload);
    } else {
      //TODO: Because I couldn't figure out how to modify the store, we just reload on the first run
      window.location.reload(true);
    }
    let idx = edges.findIndex(e => e.node.id === node.id);
    let del = 0;
    if (idx >= 0) del = 1;
    else {
      idx = 0;
      if (sameUser) {
        store.dispatch(setFlash({message: 'New run started', color: 'blue'}));
      }
    }
    edges.splice(idx, del, {node, __typename: node.__typename});
  }
  client.writeQuery({
    query: RUNS_QUERY,
    variables: queryVars,
    data,
  });
  //console.timeEnd('updating runs');
}

export default (store, client) => {
  const projectChange = watch(
      store.getState,
      'router.location.pathname',
      (cur, next) => {
        return project(cur) === project(next);
      },
    ),
    slug = params => {
      return `${params.entity}@${params.project}`;
    };
  store.subscribe(
    projectChange((newPath, oldPath) => {
      //TODO: global order state?
      const newParams = matchProject(newPath).params,
        oldParams = matchProject(oldPath).params,
        vars = {
          entityName: newParams.entity,
          name: newParams.project,
          order: 'timeline',
        };
      if (oldParams.project) {
        unsubscribe('runs-' + slug(oldParams));
      }
      if (
        newParams.project &&
        ['teams', 'admin'].indexOf(newParams.entity) < 0 &&
        ['new', 'projects'].indexOf(newParams.project) < 0
      ) {
        let queuedPayloads = [];
        let nextTimeout = null;
        let lastSubmitTime = 0;
        //Ensure we do the initial query
        client
          .query({
            query: RUNS_QUERY,
            variables: vars,
          })
          .then(() => {
            runsChannel(slug(newParams)).bind('updated', payload => {
              // We had a performance problem here where too frequent
              // updates caused the page to hang. Now we queue them up if
              // we're within a cooldown period, and do a single page update
              // for the whole batch.
              queuedPayloads.push(payload);
              let now = new Date().getTime();
              if (now - lastSubmitTime > SUBSCRIPTION_COOLDOWN) {
                updateRunsQuery(store, client, vars, queuedPayloads);
                if (nextTimeout !== null) {
                  clearTimeout(nextTimeout);
                }
                lastSubmitTime = now;
                queuedPayloads = [];
                nextTimeout = null;
              } else {
                if (nextTimeout === null) {
                  nextTimeout = setTimeout(() => {
                    updateRunsQuery(store, client, vars, queuedPayloads);
                    lastSubmitTime = new Date().getTime();
                    queuedPayloads = [];
                    nextTimeout = null;
                  }, SUBSCRIPTION_COOLDOWN);
                }
              }
            });
          });
      }
    }),
  );
};
<commit_msg>Fix double-sending of runs request.<commit_after>import watch from 'redux-watch';
import {RUNS_QUERY} from './graphql/runs';
import {matchPath} from 'react-router';
import {setFlash} from './actions';

let unsubscribe, runsChannel;
try {
  const p = require('Cloud/util/pusher');
  unsubscribe = p.unsubscribe;
  runsChannel = p.runsChannel;
} catch (e) {
  const p = require('./util/pusher');
  unsubscribe = p.unsubscribe;
  runsChannel = p.dummyChannel;
}

const SUBSCRIPTION_COOLDOWN = 20000;

function matchProject(path) {
  return matchPath(path, '/:entity/:project') || {params: {}};
}

function project(path) {
  return matchProject(path).params.project;
}

function updateRunsQuery(store, client, queryVars, payloads) {
  //console.time('updating runs');
  const state = store.getState();

  //Ensure we do the initial query
  const data = client.readQuery({
      query: RUNS_QUERY,
      variables: queryVars,
    }),
    edges = data.model.buckets.edges;

  for (var payload of payloads) {
    const sameUser = payload.user.username === state.global.user.entity;
    if (payload.state !== 'running' && sameUser) {
      //TODO: fix the finished hack
      //TODO: only dispatch if the run belongs to me
      //TODO: figure out why Lukas has his run marked as failed
      if (payload.state === 'failed') {
        console.log('failed run');
        //store.dispatch(setFlash({message: 'Run Failed', color: 'red'}));
      } else {
        store.dispatch(setFlash({message: 'Run Finished', color: 'green'}));
      }
    }

    let node;
    if (edges[0]) {
      node = Object.assign({}, edges[0].node, payload);
    } else {
      //TODO: Because I couldn't figure out how to modify the store, we just reload on the first run
      window.location.reload(true);
    }
    let idx = edges.findIndex(e => e.node.id === node.id);
    let del = 0;
    if (idx >= 0) del = 1;
    else {
      idx = 0;
      if (sameUser) {
        store.dispatch(setFlash({message: 'New run started', color: 'blue'}));
      }
    }
    edges.splice(idx, del, {node, __typename: node.__typename});
  }
  client.writeQuery({
    query: RUNS_QUERY,
    variables: queryVars,
    data,
  });
  //console.timeEnd('updating runs');
}

export default (store, client) => {
  const projectChange = watch(
      store.getState,
      'router.location.pathname',
      (cur, next) => {
        return project(cur) === project(next);
      },
    ),
    slug = params => {
      return `${params.entity}@${params.project}`;
    };
  store.subscribe(
    projectChange((newPath, oldPath) => {
      //TODO: global order state?
      const newParams = matchProject(newPath).params,
        oldParams = matchProject(oldPath).params,
        vars = {
          entityName: newParams.entity,
          name: newParams.project,
          order: 'timeline',
          requestSubscribe: true,
        };
      if (oldParams.project) {
        unsubscribe('runs-' + slug(oldParams));
      }
      if (
        newParams.project &&
        ['teams', 'admin'].indexOf(newParams.entity) < 0 &&
        ['new', 'projects'].indexOf(newParams.project) < 0
      ) {
        let queuedPayloads = [];
        let nextTimeout = null;
        let lastSubmitTime = 0;
        //Ensure we do the initial query
        client
          .query({
            query: RUNS_QUERY,
            variables: vars,
          })
          .then(() => {
            runsChannel(slug(newParams)).bind('updated', payload => {
              // We had a performance problem here where too frequent
              // updates caused the page to hang. Now we queue them up if
              // we're within a cooldown period, and do a single page update
              // for the whole batch.
              queuedPayloads.push(payload);
              let now = new Date().getTime();
              if (now - lastSubmitTime > SUBSCRIPTION_COOLDOWN) {
                updateRunsQuery(store, client, vars, queuedPayloads);
                if (nextTimeout !== null) {
                  clearTimeout(nextTimeout);
                }
                lastSubmitTime = now;
                queuedPayloads = [];
                nextTimeout = null;
              } else {
                if (nextTimeout === null) {
                  nextTimeout = setTimeout(() => {
                    updateRunsQuery(store, client, vars, queuedPayloads);
                    lastSubmitTime = new Date().getTime();
                    queuedPayloads = [];
                    nextTimeout = null;
                  }, SUBSCRIPTION_COOLDOWN);
                }
              }
            });
          });
      }
    }),
  );
};
<|endoftext|>


<commit_before>// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>
'use strict';

var _ = require('lodash');
var path = require('path');
var cordova = require('cordova');
var spawn = require('child_process').spawn;

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: 'app',
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      js: {
        files: ['<%%= yeoman.app %>/scripts/**/*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },<% if (compass) { %>
      compass: {
        files: ['<%%= yeoman.app %>/styles/**/*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },<% } else { %>
      styles: {
        files: ['<%%= yeoman.app %>/styles/**/*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },<% } %>
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/*.html',
          '<%= yeoman.app %>/templates/**/*.html',
          '.tmp/styles/**/*.css',
          '<%%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: 'www'
        }
      },
      coverage: {
        options: {
          port: 9002,
          open: true,
          base: ['coverage']
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%%= yeoman.app %>/scripts/**/*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/unit/**/*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            'www/*',
            '!www/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    'bower-install': {
      app: {
        html: '<%%= yeoman.app %>/index.html',
        ignorePath: '<%%= yeoman.app %>/'
      }
    },

    <% if (compass) { %>
    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/img/generated',
        imagesDir: '<%%= yeoman.app %>/img',
        javascriptsDir: '<%%= yeoman.app %>/scripts',
        fontsDir: '<%%= yeoman.app %>/styles/fonts',
        importPath: '<%%= yeoman.app %>/bower_components',
        httpImagesPath: '/img',
        httpGeneratedImagesPath: '/img/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%%= yeoman.dist %>/img/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },
    <% } %>

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%%= yeoman.app %>/index.html',
      options: {
        dest: 'www',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on the useminPrepare configuration
    usemin: {
      html: ['www/**/*.html'],
      css: ['www/styles/**/*.css'],
      options: {
        assetsDirs: ['www']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    cssmin: {
      options: {
        root: '<%%= yeoman.app %>',
        noRebase: true
      }
    },
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: 'www',
          src: ['*.html', 'templates/**/*.html'],
          dest: 'www'
        }]
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%%= yeoman.app %>',
          dest: 'www',
          src: [
            'images/*.{ico,png,txt}',
            '*.html',
            'templates/**/*.html',
            'images/**/*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: 'www/images',
          src: ['generated/*']
        }]
      },
      styles: {
        expand: true,
        cwd: '<%%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      },
      fonts: {
        expand: true,
        cwd: 'app/bower_components/ionic/release/fonts/',
        dest: '<%%= yeoman.app %>/fonts/',
        src: '*'
      },
      vendor: {
        expand: true,
        cwd: '<%%= yeoman.app %>/vendor',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    concurrent: {
      server: [<% if (compass) { %>
        'compass:server',<% } %>
        'copy:styles',
        'copy:vendor',
        'copy:fonts'
      ],
      test: [<% if (compass) { %>
        'compass',<% } %>
        'copy:styles',
        'copy:vendor',
        'copy:fonts'
      ],
      dist: [<% if (compass) { %>
        'compass:dist',<% } %>
        'copy:styles',
        'copy:vendor',
        'copy:fonts'
      ]
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       'www/styles/main.css': [
    //         '.tmp/styles/**/*.css',
    //         '<%%= yeoman.app %>/styles/**/*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       'www/scripts/scripts.js': [
    //         'www/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Test settings
    // These will override any config options in karma.conf.js
    karma: {
      options: {
        configFile: 'karma.conf.js',
        reporters: ['dots', 'coverage'],
        preprocessors: {
          'app/scripts/**/*.js': ['coverage']
        },
        coverageReporter: {
          reporters: [
            { type: 'html', dir: 'coverage/' },
            { type: 'text-summary' }
          ]
        }
      },
      unit: {
        // Change this to 'Chrome', 'Firefox', etc. Note that you will need
        // to install a karma launcher plugin for browsers other than Chrome.
        browsers: ['PhantomJS'],
        background: true
      },
      continuous: {
        browsers: ['PhantomJS'],
        singleRun: true,
      }
    },

    // ngmin tries to make the code safe for minification automatically by
    // using the Angular long form for dependency injection. It doesn't work on
    // things like resolve or inject so those have to be done manually.
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    }

  });

  // Register tasks for all Cordova commands, but namespace
  // the cordova:build since we already have a build task.
  _.functions(cordova).forEach(function (name) {
    name = (name === 'build') ? 'cordova:build' : name;
    grunt.registerTask(name, function () {
      this.args.unshift(name.replace('cordova:', ''));
      // Handle URL's being split up by Grunt because of `:` characters
      if (_.contains(this.args, 'http') || _.contains(this.args, 'https')) {
        this.args = this.args.slice(0, -2).concat(_.last(this.args, 2).join(':'));
      }
      var done = this.async();
      var cmd = path.resolve('./node_modules/cordova/bin', '<%= process.platform === 'win32' ? 'cordova.cmd' : 'cordova' %>');
      var child = spawn(cmd, this.args);
      child.stdout.on('data', function (data) {
        grunt.log.writeln(data);
      });
      child.stderr.on('data', function (data) {
        grunt.log.error(data);
      });
      child.on('close', function (code) {
        code = (name === 'cordova:build') ? true : code ? false : true;
        done(code);
      });
    });
  });

  // Dynamically configure `karma` target of `watch` task so that
  // we don't have to run the karma test server as part of `grunt serve`
  grunt.registerTask('watch:karma', function () {
    var karma = {
      files: ['<%%= yeoman.app %>/scripts/**/*.js', 'test/spec/**/*.js'],
      tasks: ['newer:jshint:test', 'karma:unit:run']
    };
    grunt.config.set('watch', karma);
    return grunt.task.run(['watch']);
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'bower-install',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'karma:unit:start',
    'watch:karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'bower-install',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cssmin',
    'uglify',
    'usemin',
    'htmlmin',
    'cordova:build'
  ]);

  grunt.registerTask('coverage', ['karma:continuous', 'connect:coverage:keepalive']);

  grunt.registerTask('default', [
    'newer:jshint',
    'karma:continuous',
    'build'
  ]);
};
<commit_msg>fix(grunt): Copy over app/res to www/res to include icons & splash screen assets.<commit_after>// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>
'use strict';

var _ = require('lodash');
var path = require('path');
var cordova = require('cordova');
var spawn = require('child_process').spawn;

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: 'app',
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      js: {
        files: ['<%%= yeoman.app %>/scripts/**/*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },<% if (compass) { %>
      compass: {
        files: ['<%%= yeoman.app %>/styles/**/*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },<% } else { %>
      styles: {
        files: ['<%%= yeoman.app %>/styles/**/*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },<% } %>
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/*.html',
          '<%= yeoman.app %>/templates/**/*.html',
          '.tmp/styles/**/*.css',
          '<%%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: 'www'
        }
      },
      coverage: {
        options: {
          port: 9002,
          open: true,
          base: ['coverage']
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%%= yeoman.app %>/scripts/**/*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/unit/**/*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            'www/*',
            '!www/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    'bower-install': {
      app: {
        html: '<%%= yeoman.app %>/index.html',
        ignorePath: '<%%= yeoman.app %>/'
      }
    },

    <% if (compass) { %>
    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/img/generated',
        imagesDir: '<%%= yeoman.app %>/img',
        javascriptsDir: '<%%= yeoman.app %>/scripts',
        fontsDir: '<%%= yeoman.app %>/styles/fonts',
        importPath: '<%%= yeoman.app %>/bower_components',
        httpImagesPath: '/img',
        httpGeneratedImagesPath: '/img/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%%= yeoman.dist %>/img/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },
    <% } %>

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%%= yeoman.app %>/index.html',
      options: {
        dest: 'www',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on the useminPrepare configuration
    usemin: {
      html: ['www/**/*.html'],
      css: ['www/styles/**/*.css'],
      options: {
        assetsDirs: ['www']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    cssmin: {
      options: {
        root: '<%%= yeoman.app %>',
        noRebase: true
      }
    },
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: 'www',
          src: ['*.html', 'templates/**/*.html'],
          dest: 'www'
        }]
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%%= yeoman.app %>',
          dest: 'www',
          src: [
            'images/*.{ico,png,txt}',
            '*.html',
            'templates/**/*.html',
            'images/**/*.{webp}',
            'fonts/*',
            'res/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: 'www/images',
          src: ['generated/*']
        }]
      },
      styles: {
        expand: true,
        cwd: '<%%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      },
      fonts: {
        expand: true,
        cwd: 'app/bower_components/ionic/release/fonts/',
        dest: '<%%= yeoman.app %>/fonts/',
        src: '*'
      },
      vendor: {
        expand: true,
        cwd: '<%%= yeoman.app %>/vendor',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    concurrent: {
      server: [<% if (compass) { %>
        'compass:server',<% } %>
        'copy:styles',
        'copy:vendor',
        'copy:fonts'
      ],
      test: [<% if (compass) { %>
        'compass',<% } %>
        'copy:styles',
        'copy:vendor',
        'copy:fonts'
      ],
      dist: [<% if (compass) { %>
        'compass:dist',<% } %>
        'copy:styles',
        'copy:vendor',
        'copy:fonts'
      ]
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       'www/styles/main.css': [
    //         '.tmp/styles/**/*.css',
    //         '<%%= yeoman.app %>/styles/**/*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       'www/scripts/scripts.js': [
    //         'www/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Test settings
    // These will override any config options in karma.conf.js
    karma: {
      options: {
        configFile: 'karma.conf.js',
        reporters: ['dots', 'coverage'],
        preprocessors: {
          'app/scripts/**/*.js': ['coverage']
        },
        coverageReporter: {
          reporters: [
            { type: 'html', dir: 'coverage/' },
            { type: 'text-summary' }
          ]
        }
      },
      unit: {
        // Change this to 'Chrome', 'Firefox', etc. Note that you will need
        // to install a karma launcher plugin for browsers other than Chrome.
        browsers: ['PhantomJS'],
        background: true
      },
      continuous: {
        browsers: ['PhantomJS'],
        singleRun: true,
      }
    },

    // ngmin tries to make the code safe for minification automatically by
    // using the Angular long form for dependency injection. It doesn't work on
    // things like resolve or inject so those have to be done manually.
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    }

  });

  // Register tasks for all Cordova commands, but namespace
  // the cordova:build since we already have a build task.
  _.functions(cordova).forEach(function (name) {
    name = (name === 'build') ? 'cordova:build' : name;
    grunt.registerTask(name, function () {
      this.args.unshift(name.replace('cordova:', ''));
      // Handle URL's being split up by Grunt because of `:` characters
      if (_.contains(this.args, 'http') || _.contains(this.args, 'https')) {
        this.args = this.args.slice(0, -2).concat(_.last(this.args, 2).join(':'));
      }
      var done = this.async();
      var cmd = path.resolve('./node_modules/cordova/bin', '<%= process.platform === 'win32' ? 'cordova.cmd' : 'cordova' %>');
      var child = spawn(cmd, this.args);
      child.stdout.on('data', function (data) {
        grunt.log.writeln(data);
      });
      child.stderr.on('data', function (data) {
        grunt.log.error(data);
      });
      child.on('close', function (code) {
        code = (name === 'cordova:build') ? true : code ? false : true;
        done(code);
      });
    });
  });

  // Dynamically configure `karma` target of `watch` task so that
  // we don't have to run the karma test server as part of `grunt serve`
  grunt.registerTask('watch:karma', function () {
    var karma = {
      files: ['<%%= yeoman.app %>/scripts/**/*.js', 'test/spec/**/*.js'],
      tasks: ['newer:jshint:test', 'karma:unit:run']
    };
    grunt.config.set('watch', karma);
    return grunt.task.run(['watch']);
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'bower-install',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'karma:unit:start',
    'watch:karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'bower-install',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cssmin',
    'uglify',
    'usemin',
    'htmlmin',
    'cordova:build'
  ]);

  grunt.registerTask('coverage', ['karma:continuous', 'connect:coverage:keepalive']);

  grunt.registerTask('default', [
    'newer:jshint',
    'karma:continuous',
    'build'
  ]);
};
<|endoftext|>


<commit_before>'use strict';
/* jshint camelcase: false */

var config = require('./config');
var gulp = require('gulp');

/**
 * Dev Task File
 *
 */

var sass = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var sourcemaps = require('gulp-sourcemaps');
var wiredep = require('wiredep').stream;
var inj = require('gulp-inject');

var browserSync = require('browser-sync');
var reload      = browserSync.reload;
var watch = require('gulp-watch');
var runSequence = require('run-sequence').use(gulp);

var jshint = require('gulp-jshint');
var jscs = require('gulp-jscs');
var karma = require('gulp-karma');
var webdriver_standalone = require('gulp-protractor').webdriver_standalone;
var protractor = require('gulp-protractor').protractor;
gulp.task('webdriver_standalone', webdriver_standalone);

function swallowError(error)
{
    console.log(error.toString());
    this.emit('end');
}

// main task
gulp.task('default', function ()
{
    runSequence(
        'injectAll',
        'buildStyles',
        'browserSync',
        'test',
        'watch'
    );
});
gulp.task('serve', ['default']);
gulp.task('server', ['default']);

gulp.task('injectAll', function ()
{
    runSequence(
        'wiredep',
        'injectScripts',
        'injectStyles'
    );
});

gulp.task('watch', function ()
{
    watch(config.stylesF, function ()
    {
        gulp.start('buildStyles');
    });
    watch(config.scriptsF, function ()
    {
        gulp.start('injectScripts');
    });
    watch(config.scriptsAllF, function ()
    {
        gulp.start('lint');
    });

    watch(config.allHtmlF, function ()
    {
        gulp.start('html');
    });

    gulp.watch('bower.json', ['wiredep']);
});

gulp.task('buildStyles', function ()
{
    runSequence(
        'injectStyles',
        'sass'
    );
});

gulp.task('injectStyles', function ()
{
    var sources = gulp.src(config.stylesF, {read: false});
    var target = gulp.src(config.mainSassFile);
    var outputFolder = gulp.dest(config.styles);

    target
        .pipe(inj(sources,
            {
                starttag: '// inject:sass',
                endtag: '// endinject',
                ignorePath: [config.base.replace('./', ''), 'styles'],
                relative: true,
                addRootSlash: false,
                transform: function (filepath)
                {
                    if (filepath) {
                        return '@import  \'' + filepath + '\';';
                    }
                }
            }
        ))
        .pipe(outputFolder);
});

gulp.task('injectScripts', function ()
{
    var sources = gulp.src(config.scriptsF, {read: false});
    var target = gulp.src(config.mainFile);
    target
        .pipe(inj(sources,
            {
                ignorePath: config.base.replace('./', ''),
                addRootSlash: false
            }
        ))
        .pipe(gulp.dest(config.base))
        .pipe(reload({stream: true}));
});

gulp.task('sass', function ()
{
    var sources = gulp.src(config.mainSassFile);
    var outputFolder = gulp.dest(config.styles);

    sources
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(autoprefixer({
            browsers: ['last 2 versions']
        }))
        .pipe(sourcemaps.write())
        .pipe(outputFolder)
        .pipe(reload({stream: true}))
        .on('error', swallowError)
});

gulp.task('browserSync', function ()
{
    browserSync({
        server: {
            baseDir: config.base,
            livereload: true
        }
    });
});

gulp.task('html', function ()
{
    gulp.src(config.allHtmlF)
        .pipe(reload({stream: true}));
});

gulp.task('wiredep', function ()
{
    gulp.src([config.mainFile], {base: './'})
        .pipe(wiredep({
            exclude: [],
            devDependencies: false
        }))
        .pipe(gulp.dest('./'));

    gulp.src([config.karmaConf], {base: './'})
        .pipe(wiredep({
            exclude: [],
            devDependencies: true
        }))
        .pipe(gulp.dest('./'));
});

gulp.task('test', function ()
{
    // Be sure to return the stream
    gulp.src('.nonononoNOTHING')
        .pipe(karma({
            configFile: './karma.conf.js',
            action: 'watch'
        }))
        .on('error', function (err)
        {
            throw err;
        })
        .on('error', swallowError);
});

gulp.task('testSingle', function ()
{
    // Be sure to return the stream
    gulp.src('.nonononoNOTHING')
        .pipe(karma({
            configFile: './karma.conf.js',
            action: 'run'
        }))
        .on('error', function (err)
        {
            throw err;
        });
});

gulp.task('protractor', function ()
{
    // Be sure to return the stream
    gulp.src('.nonononoNOTHING')
        .pipe(protractor({
            configFile: './karma-e2e.conf.js',
            args: ['--baseUrl', e2eBaseUrl]
        }))
        .on('error', function (e)
        {
            throw e;
        });
});

gulp.task('lint', function ()
{
    gulp.src([
        config.scriptsAllF,
        './karma-e2e.conf.js',
        './karma.conf.js',
        './gulpfile.js'
    ])
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'))
        .pipe(jscs());
});

gulp.task('e2e', [
    'browserSync',
    'protractor'
]);
<commit_msg>fix wiredep / injectScripts conflict<commit_after>'use strict';
/* jshint camelcase: false */

var config = require('./config');
var gulp = require('gulp');

/**
 * Dev Task File
 *
 */

var sass = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var sourcemaps = require('gulp-sourcemaps');
var wiredep = require('wiredep').stream;
var inj = require('gulp-inject');

var browserSync = require('browser-sync');
var reload = browserSync.reload;
var watch = require('gulp-watch');
var runSequence = require('run-sequence').use(gulp);

var jshint = require('gulp-jshint');
var jscs = require('gulp-jscs');
var karma = require('gulp-karma');

function swallowError(error)
{
    console.log(error.toString());
    this.emit('end');
}

// main task
gulp.task('default', function ()
{
    runSequence(
        'injectAll',
        'buildStyles',
        'test',
        'browserSync',
        'watch'
    );
});
gulp.task('serve', ['default']);
gulp.task('server', ['default']);

gulp.task('injectAll', function (callback)
{
    runSequence(
        'wiredep',
        'injectScripts',
        'injectStyles',
        callback
    );
});

gulp.task('watch', function ()
{
    watch(config.stylesF, function ()
    {
        gulp.start('buildStyles');
    });
    watch(config.scriptsF, function ()
    {
        gulp.start('injectScripts');
    });
    watch(config.scriptsAllF, function ()
    {
        gulp.start('lint');
    });

    watch(config.allHtmlF, function ()
    {
        gulp.start('html');
    });

    gulp.watch('bower.json', ['wiredep']);
});

gulp.task('buildStyles', function ()
{
    runSequence(
        'injectStyles',
        'sass'
    );
});

gulp.task('injectStyles', function ()
{
    var sources = gulp.src(config.stylesF, {read: false});
    var target = gulp.src(config.mainSassFile);
    var outputFolder = gulp.dest(config.styles);

    target
        .pipe(inj(sources,
            {
                starttag: '// inject:sass',
                endtag: '// endinject',
                ignorePath: [config.base.replace('./', ''), 'styles'],
                relative: true,
                addRootSlash: false,
                transform: function (filepath)
                {
                    if (filepath) {
                        return '@import  \'' + filepath + '\';';
                    }
                }
            }
        ))
        .pipe(outputFolder);
});

gulp.task('injectScripts', function ()
{
    var sources = gulp.src(config.scriptsF, {read: false});
    var target = gulp.src(config.mainFile);
    target
        .pipe(inj(sources,
            {
                ignorePath: config.base.replace('./', ''),
                addRootSlash: false
            }
        ))
        .pipe(gulp.dest(config.base));
});

gulp.task('sass', function ()
{
    var sources = gulp.src(config.mainSassFile);
    var outputFolder = gulp.dest(config.styles);

    sources
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(autoprefixer({
            browsers: ['last 2 versions']
        }))
        .pipe(sourcemaps.write())
        .pipe(outputFolder)
        .pipe(reload({stream: true}))
        .on('error', swallowError)
});

gulp.task('browserSync', function ()
{
    browserSync({
        server: {
            baseDir: config.base,
            livereload: true
        }
    });
});

gulp.task('html', function ()
{
    gulp.src(config.allHtmlF)
        .pipe(reload({stream: true}));
});

gulp.task('wiredep', function ()
{
    gulp.src([config.mainFile], {base: './'})
        .pipe(wiredep({
            devDependencies: false
        }))
        .pipe(gulp.dest('./'))
        .pipe(reload({stream: true}));

    gulp.src([config.karmaConf], {base: './'})
        .pipe(wiredep({
            devDependencies: true
        }))
        .pipe(gulp.dest('./'));
});

gulp.task('test', function ()
{
    // Be sure to return the stream
    gulp.src('.nonononoNOTHING')
        .pipe(karma({
            configFile: './karma.conf.js',
            action: 'watch'
        }))
        .on('error', function (err)
        {
            throw err;
        })
        .on('error', swallowError);
});

gulp.task('testSingle', function ()
{
    // Be sure to return the stream
    gulp.src('.nonononoNOTHING')
        .pipe(karma({
            configFile: './karma.conf.js',
            action: 'run'
        }))
        .on('error', function (err)
        {
            throw err;
        });
});

gulp.task('lint', function ()
{
    gulp.src([
        config.scriptsAllF,
        './karma-e2e.conf.js',
        './karma.conf.js',
        './gulpfile.js'
    ])
        .pipe(jshint())
        .pipe(jshint.reporter('jshint-stylish'))
        .pipe(jscs());
});
<|endoftext|>


<commit_before><commit_msg>updating input<commit_after>/*
    Input controller
*/
"use strict";

var calc = require('../utils/calc.js'),
    utils = require('../utils/utils.js'),
    History = require('../bobs/history.js'),

    /*
        Input constructor

            Syntax
                newInput(name, value)
                    @param [string]: Name of to track
                    @param [number]: Initial value

                newInput(props)
                    @param [object]: Object of values

        @return [Input]
    */
    Input = function () {
        this.current = {};
        this.offset = {};
        this.velocity = {};
        this.history = new History();
        this.update(arguments[0], arguments[1]);
    };

Input.prototype = {

    // [number]: Number of frames of inactivity before velocity is turned to 0
    maxInactiveFrames: 2,

    // [number]: Number of frames input hasn't been updated
    inactiveFrames: 0,

    /*
        Get latest input values

        @param [string] (optional): Name of specific property to return
        @return [object || number]: Latest input values or, if specified, single value
    */
    get: function (prop) {
        var latest = this.history.get(),
            val = (prop !== undefined) ? latest[prop] : latest;

        return val;
    },

    /*
        Update the input values

        Syntax
            input.update(name, value)
                @param [string]: Name of to track
                @param [number]: Initial value

            input.update(props)
                @param [object]: Object of values

        @return [Input]
    */
    update: function () {
        var values = {};

        if (utils.isNum(arguments[1])) {
            values[arguments[0]] = arguments[1];
        } else {
            values = arguments[0];
        }

        this.history.add(utils.merge(this.current, values));

        return this;
    },

    /*
        Check for input movement and update pointer object's properties

        @param [number]: Timestamp of frame
        @return [Input]
    */
    onFrame: function (timestamp) {
        var latest, hasChanged;

        // Check provided timestamp against lastFrame timestamp and return input has already been updated
        if (timestamp === this.lastFrame) {
            return;
        }

        latest = this.history.get();
        hasChanged = utils.hasChanged(this.current, latest);

        // If input has changed between frames
        if (hasChanged) {
            this.velocity = calc.offset(this.current, latest);
            this.current = latest;
            this.inactiveFrames = 0;

        // Or it hasn't moved and our frame limit has been reached
        } else if (this.inactiveFrames >= this.maxInactiveFrames) {
            this.velocity = calc.offset(this.current, this.current);

        // Or input hasn't changed
        } else {
            this.inactiveFrames++;
        }

        this.lastFrame = timestamp;

        return this;
    }

};

module.exports = Input;<|endoftext|>


<commit_before>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name PlacedSymbol
 *
 * @class A PlacedSymbol represents an instance of a symbol which has been
 * placed in a Paper.js project.
 *
 * @extends PlacedItem
 */
var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend(/** @lends PlacedSymbol# */{
	_type: 'placedsymbol',
	_boundsSelected: true,
	_serializeFields: {
		symbol: null
	},

	/**
	 * Creates a new PlacedSymbol Item.
	 *
	 * @param {Symbol} symbol the symbol to place
	 * @param {Point} [point] the center point of the placed symbol
	 *
	 * @example {@paperscript split=true height=240}
	 * // Placing 100 instances of a symbol:
	 * var path = new Path.Star(new Point(0, 0), 6, 5, 13);
	 * path.style = {
	 *     fillColor: 'white',
	 *     strokeColor: 'black'
	 * };
     *
	 * // Create a symbol from the path:
	 * var symbol = new Symbol(path);
	 *
	 * // Remove the path:
	 * path.remove();
     *
	 * // Place 100 instances of the symbol:
	 * for (var i = 0; i < 100; i++) {
	 *     // Place an instance of the symbol in the project:
	 *     var instance = new PlacedSymbol(symbol);
     *
	 *     // Move the instance to a random position within the view:
	 *     instance.position = Point.random() * view.size;
     *
	 *     // Rotate the instance by a random amount between
	 *     // 0 and 360 degrees:
	 *     instance.rotate(Math.random() * 360);
     *
	 *     // Scale the instance between 0.25 and 1:
	 *     instance.scale(0.25 + Math.random() * 0.75);
	 * }
	 */
	initialize: function(arg0, arg1) {
		// Support two forms of item initialization: Passing one object literal
		// describing all the different properties to be set, or a symbol (arg0)
		// and a point where it should be placed (arg1).
		this.base(arg1 !== undefined && Point.read(arguments, 1));
		// If we can handle setting properties through object literal, we're all
		// set. Otherwise we need to set symbol.
		if (!this._set(arg0))
			this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
	},

	/**
	 * The symbol that the placed symbol refers to.
	 *
	 * @type Symbol
	 * @bean
	 */
	getSymbol: function() {
		return this._symbol;
	},

	setSymbol: function(symbol) {
		// Remove from previous symbol's instances
		if (this._symbol)
			delete this._symbol._instances[this._id];
		this._symbol = symbol;
		// Add to the new one's
		symbol._instances[this._id] = this;
	},

	isEmpty: function() {
		return this._symbol._definition.isEmpty();
	},

	clone: function() {
		return this._clone(new PlacedSymbol(this.symbol, this._matrix.clone()));
	},

	_getBounds: function(getter, matrix) {
		// Redirect the call to the symbol definition to calculate the bounds
		// TODO: Implement bounds caching through passing on of cacheItem, so
		// that Symbol#_changed() notification become unnecessary!
		return this.symbol._definition._getCachedBounds(getter, matrix);
	},

	draw: function(ctx, param) {
		Item.draw(this.symbol._definition, ctx, param);
	}
	// TODO: PlacedSymbol#embed()
});
<commit_msg>No need to clone _matrix, since _clone() does that for us.<commit_after>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name PlacedSymbol
 *
 * @class A PlacedSymbol represents an instance of a symbol which has been
 * placed in a Paper.js project.
 *
 * @extends PlacedItem
 */
var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend(/** @lends PlacedSymbol# */{
	_type: 'placedsymbol',
	_boundsSelected: true,
	_serializeFields: {
		symbol: null
	},

	/**
	 * Creates a new PlacedSymbol Item.
	 *
	 * @param {Symbol} symbol the symbol to place
	 * @param {Point} [point] the center point of the placed symbol
	 *
	 * @example {@paperscript split=true height=240}
	 * // Placing 100 instances of a symbol:
	 * var path = new Path.Star(new Point(0, 0), 6, 5, 13);
	 * path.style = {
	 *     fillColor: 'white',
	 *     strokeColor: 'black'
	 * };
     *
	 * // Create a symbol from the path:
	 * var symbol = new Symbol(path);
	 *
	 * // Remove the path:
	 * path.remove();
     *
	 * // Place 100 instances of the symbol:
	 * for (var i = 0; i < 100; i++) {
	 *     // Place an instance of the symbol in the project:
	 *     var instance = new PlacedSymbol(symbol);
     *
	 *     // Move the instance to a random position within the view:
	 *     instance.position = Point.random() * view.size;
     *
	 *     // Rotate the instance by a random amount between
	 *     // 0 and 360 degrees:
	 *     instance.rotate(Math.random() * 360);
     *
	 *     // Scale the instance between 0.25 and 1:
	 *     instance.scale(0.25 + Math.random() * 0.75);
	 * }
	 */
	initialize: function(arg0, arg1) {
		// Support two forms of item initialization: Passing one object literal
		// describing all the different properties to be set, or a symbol (arg0)
		// and a point where it should be placed (arg1).
		this.base(arg1 !== undefined && Point.read(arguments, 1));
		// If we can handle setting properties through object literal, we're all
		// set. Otherwise we need to set symbol.
		if (!this._set(arg0))
			this.setSymbol(arg0 instanceof Symbol ? arg0 : new Symbol(arg0));
	},

	/**
	 * The symbol that the placed symbol refers to.
	 *
	 * @type Symbol
	 * @bean
	 */
	getSymbol: function() {
		return this._symbol;
	},

	setSymbol: function(symbol) {
		// Remove from previous symbol's instances
		if (this._symbol)
			delete this._symbol._instances[this._id];
		this._symbol = symbol;
		// Add to the new one's
		symbol._instances[this._id] = this;
	},

	isEmpty: function() {
		return this._symbol._definition.isEmpty();
	},

	clone: function() {
		return this._clone(new PlacedSymbol(this.symbol));
	},

	_getBounds: function(getter, matrix) {
		// Redirect the call to the symbol definition to calculate the bounds
		// TODO: Implement bounds caching through passing on of cacheItem, so
		// that Symbol#_changed() notification become unnecessary!
		return this.symbol._definition._getCachedBounds(getter, matrix);
	},

	draw: function(ctx, param) {
		Item.draw(this.symbol._definition, ctx, param);
	}

	// TODO: PlacedSymbol#embed()
});
<|endoftext|>


<commit_before>
$.each({
    dialogPrepare: function(options){
                       /*
                        * This function creates a new dialog and makes sure other dialog-related functions will
                        * work perfectly with it
                        */
        var dialog=$('

').appendTo('body');
        if(options.noAutoSizeHack)dialog.removeClass('dialog_autosize');
        dialog.dialog(options);
        if(options.customClass){
            dialog.parent().addClass(options.customClass);
        }
        $.data(dialog.get(0),'opener',this.jquery);
        $.data(dialog.get(0),'options',options);

        return dialog;
    },
    getDialogData: function(key){
        var dlg=this.jquery.closest('.dialog').get(0);
        if(!dlg)return null;
        var r=$.data(dlg,key);
        if(!r){
            return null;
        }
        return r;
    },
    getFrameOpener: function(){
        var d=this.getDialogData('opener');
        if(!d)return null;
        return $(this.getDialogData('opener'));
    },
    dialogBox: function(options){

        if (!options.ok_label) options.ok_label = 'Ok';
        if (!options.ok_class) options.ok_class = 'atk-effect-primary';

        var buttons=[];

        buttons.push({
            text: options.ok_label,
            class: options.ok_class,
            click: function(){
                var f=$(this).find('form');
                if(f.length)f.eq(0).submit(); else $(this).dialog('close');
            }
        });
        buttons.push({
            text: 'Cancel',
            click: function(){
                $(this).dialog('close');
            }
        });

        return this.dialogPrepare($.extend({
            bgiframe: true,
            modal: true,
            width: 1000,
            position: { my:'top',at:'top+100','of':window },
            autoOpen:false,
            beforeclose: function(){
                if($(this).is('.atk4_loader')){
                    if(!$(this).atk4_loader('remove'))return false;
                }
            },
            buttons: buttons,
            open: function(x){
                $("body").css({ overflow: 'hidden' });
                $(x.target).css({'max-height': $(window).height()-180});
            },
            close: function(){
                $("body").css({ overflow: 'auto' });
                $(this).dialog('destroy');
                $(this).remove();
            }
        },options));
    },
    dialogURL: function(title,url,options,callback){
        if(typeof url == 'undefined'){
            url=title;
            title='Untitled Dialog';
        }
        var dlg=this.dialogBox($.extend(options,{title: title,autoOpen: true}));
        dlg.closest('.ui-dialog').hide().fadeIn('slow');
        dlg.atk4_load(url,callback);
        return dlg.dialog('open');
    },
    frameURL: function(title,url,options,callback){
        options=$.extend({
            buttons:{}
        },options);
        return this.dialogURL(title,url,options,callback);
    },
    dialogOK: function(title,text,fn,options){
        var dlg=this.dialogBox($.extend({
            title: title,
            width: 450,
            //height: 150,
            close: fn,
            open: function() {
                $(this).parents('.ui-dialog-buttonpane button:eq(0)').focus();
            },
            buttons: {
                'Ok': function(){
                    $(this).dialog('close');
                }
            }
        },options));
        dlg.html(text);
        dlg.dialog('open');

    },
    dialogConfirm: function(title,text,fn,options){
        /*
         * Displays confirmation dialogue.
         */
        var dlg=this.dialogBox($.extend({title: title, width: 450, height: 200},options));

        dlg.html("<form></form>"+text);
        dlg.find('form').submit(function(ev){ ev.preventDefault(); if(fn)fn(); dlg.dialog('close'); });
        dlg.dialog('open');
    },
    dialogError: function(text,options,fn){
        this.dialogConfirm('Error',''+text,null,
                           $.extend({buttons:{'Ok':function(){ $(this).dialog('close');if(fn)fn()}}},options));
    },
    dialogAttention: function(text,options,fn){
        this.dialogConfirm('Attention!',''+text,null,
                           $.extend({buttons:{'Ok':function(){ $(this).dialog('close');if(fn)fn()}}},options));
    },
    message: function(msg,icon){
        if($.ui.atk4_notify && $('#atk-growl-holder').length){
            $('#atk-growl-holder').atk4_notify().atk4_notify('message',msg,icon);
            return;
        }
    },
    successMessage: function(msg){
        var html="";

        if($.ui.atk4_notify && $('#atk-growl-holder').length){
            $('#atk-growl-holder').atk4_notify().atk4_notify('successMessage',msg);
            return;
        }

        return alert(msg);
    },
    errorMessage: function(msg){
        var html="";

        if($.ui.atk4_notify && $('#atk-growl-holder').length){
            $('#atk-growl-holder').atk4_notify().atk4_notify('errorMessage',msg);
            return;
        }

        return alert(msg);
    },
    closeDialog: function(){
        var r=this.getFrameOpener();
        if(!r)return;
        this.jquery.closest('.dialog').dialog('close');
        this.jquery=r;
    },
    loadingInProgress: function(){
        this.successMessage('Loading is in progress. Please wait');
    }
},$.univ._import);

var oldcr = $.ui.dialog.prototype._create;
$.ui.dialog.prototype._create = function(){
    var self=this;
    $('
').insertBefore(this.element).on('remove',function(){
        self.element.remove();
    });
    oldcr.apply(this,arguments);
};

<commit_msg>class is a reserved word<commit_after>
$.each({
    dialogPrepare: function(options){
                       /*
                        * This function creates a new dialog and makes sure other dialog-related functions will
                        * work perfectly with it
                        */
        var dialog=$('

').appendTo('body');
        if(options.noAutoSizeHack)dialog.removeClass('dialog_autosize');
        dialog.dialog(options);
        if(options.customClass){
            dialog.parent().addClass(options.customClass);
        }
        $.data(dialog.get(0),'opener',this.jquery);
        $.data(dialog.get(0),'options',options);

        return dialog;
    },
    getDialogData: function(key){
        var dlg=this.jquery.closest('.dialog').get(0);
        if(!dlg)return null;
        var r=$.data(dlg,key);
        if(!r){
            return null;
        }
        return r;
    },
    getFrameOpener: function(){
        var d=this.getDialogData('opener');
        if(!d)return null;
        return $(this.getDialogData('opener'));
    },
    dialogBox: function(options){

        if (!options.ok_label) options.ok_label = 'Ok';
        if (!options.ok_class) options.ok_class = 'atk-effect-primary';

        var buttons=[];

        buttons.push({
            text: options.ok_label,
            'class': options.ok_class,
            click: function(){
                var f=$(this).find('form');
                if(f.length)f.eq(0).submit(); else $(this).dialog('close');
            }
        });
        buttons.push({
            text: 'Cancel',
            click: function(){
                $(this).dialog('close');
            }
        });

        return this.dialogPrepare($.extend({
            bgiframe: true,
            modal: true,
            width: 1000,
            position: { my:'top',at:'top+100','of':window },
            autoOpen:false,
            beforeclose: function(){
                if($(this).is('.atk4_loader')){
                    if(!$(this).atk4_loader('remove'))return false;
                }
            },
            buttons: buttons,
            open: function(x){
                $("body").css({ overflow: 'hidden' });
                $(x.target).css({'max-height': $(window).height()-180});
            },
            close: function(){
                $("body").css({ overflow: 'auto' });
                $(this).dialog('destroy');
                $(this).remove();
            }
        },options));
    },
    dialogURL: function(title,url,options,callback){
        if(typeof url == 'undefined'){
            url=title;
            title='Untitled Dialog';
        }
        var dlg=this.dialogBox($.extend(options,{title: title,autoOpen: true}));
        dlg.closest('.ui-dialog').hide().fadeIn('slow');
        dlg.atk4_load(url,callback);
        return dlg.dialog('open');
    },
    frameURL: function(title,url,options,callback){
        options=$.extend({
            buttons:{}
        },options);
        return this.dialogURL(title,url,options,callback);
    },
    dialogOK: function(title,text,fn,options){
        var dlg=this.dialogBox($.extend({
            title: title,
            width: 450,
            //height: 150,
            close: fn,
            open: function() {
                $(this).parents('.ui-dialog-buttonpane button:eq(0)').focus();
            },
            buttons: {
                'Ok': function(){
                    $(this).dialog('close');
                }
            }
        },options));
        dlg.html(text);
        dlg.dialog('open');

    },
    dialogConfirm: function(title,text,fn,options){
        /*
         * Displays confirmation dialogue.
         */
        var dlg=this.dialogBox($.extend({title: title, width: 450, height: 200},options));

        dlg.html("<form></form>"+text);
        dlg.find('form').submit(function(ev){ ev.preventDefault(); if(fn)fn(); dlg.dialog('close'); });
        dlg.dialog('open');
    },
    dialogError: function(text,options,fn){
        this.dialogConfirm('Error',''+text,null,
                           $.extend({buttons:{'Ok':function(){ $(this).dialog('close');if(fn)fn()}}},options));
    },
    dialogAttention: function(text,options,fn){
        this.dialogConfirm('Attention!',''+text,null,
                           $.extend({buttons:{'Ok':function(){ $(this).dialog('close');if(fn)fn()}}},options));
    },
    message: function(msg,icon){
        if($.ui.atk4_notify && $('#atk-growl-holder').length){
            $('#atk-growl-holder').atk4_notify().atk4_notify('message',msg,icon);
            return;
        }
    },
    successMessage: function(msg){
        var html="";

        if($.ui.atk4_notify && $('#atk-growl-holder').length){
            $('#atk-growl-holder').atk4_notify().atk4_notify('successMessage',msg);
            return;
        }

        return alert(msg);
    },
    errorMessage: function(msg){
        var html="";

        if($.ui.atk4_notify && $('#atk-growl-holder').length){
            $('#atk-growl-holder').atk4_notify().atk4_notify('errorMessage',msg);
            return;
        }

        return alert(msg);
    },
    closeDialog: function(){
        var r=this.getFrameOpener();
        if(!r)return;
        this.jquery.closest('.dialog').dialog('close');
        this.jquery=r;
    },
    loadingInProgress: function(){
        this.successMessage('Loading is in progress. Please wait');
    }
},$.univ._import);

var oldcr = $.ui.dialog.prototype._create;
$.ui.dialog.prototype._create = function(){
    var self=this;
    $('
').insertBefore(this.element).on('remove',function(){
        self.element.remove();
    });
    oldcr.apply(this,arguments);
};

<|endoftext|>


<commit_before>$(function() {
  $('button.atp').prop('title', 'Add/Remove to/from Project').button({ icons: { primary: 'ui-icon-note' }, text: false }).unbind('click').click(function() {
    _load_project_dialog($(this).attr('ty'), $(this).attr('iid'), $(this).attr('name'))
  })

  var c = $.cookie('ispyb_help')
  var help_def = ($(window).width() <= 800) ? 0 : 1
  var help = c === undefined ? help_def : c

  $('[title]').each(function(i,e) {
    $(e).attr('t_old', $(e).attr('title'))
  })

  $.editable.addInputType('autocomplete', {
      element : $.editable.types.text.element,
      plugin : function(settings, original) {
          $('input', this).autocomplete(settings.autocomplete);
      }
  })

  /*$('.current').editable('/proposal/ajax/set/', {
    type: 'autocomplete',
    autocomplete: { source: '/proposal/ajax/p/' },
    name: 'prop',
    width: '60px',
    submit: 'Ok',
    style: 'display: inline',
    placeholder: 'Click to Select',
    //onblur: 'ignore',
    callback: function() { window.location.href = '/proposal/visits' },
  }).addClass('editable');*/

  //$('#sidebar a.pull').click(function() {
  //  $('#sidebar ul').slideToggle()
  //})

  $('a.pull').addClass('enable').click(function() { $('body').toggleClass('active'); return false })

  $('#sidebar ul li.help a').click(function() {
    help = help == 1 ? 0 : 1;
    $.cookie('ispyb_help', help);
    _toggle_help()

    return false;
  })

  function _toggle_help() {
    if (help == 1) {
      $('#sidebar ul li.help').addClass('active')
      $('[title]').tipTip({delay:100, fadeIn: 400, defaultPosition: 'top'})
      $('[t_old]').each(function(i,e) { $(e).data('tt_disable', false) })
      $('p.help').fadeIn()

    } else {
      $('#sidebar ul li.help').removeClass('active')
      $('[t_old]').each(function(i,e) { $(e).data('tt_disable', true) })
      $('p.help').fadeOut()
    }
  }

  _toggle_help()

  $(document).ajaxComplete(function() {
    $('button.atp').prop('title', 'Add/Remove to/from Project').button({ icons: { primary: 'ui-icon-note' }, text: false }).unbind('click').click(function() {
      _load_project_dialog($(this).attr('ty'), $(this).attr('iid'), $(this).attr('name'))
    })

    $('[title]').each(function(i,e) {
      $(e).attr('t_old', $(e).attr('title'))
    })
    _toggle_help()
  })

  // Add to project popup
  $('.project').dialog({ title: 'Add To Project', autoOpen: false, height: 'auto', width: 'auto' });

  $('.project select[name=pid]').change(function() { _check_project_item })
  // Check whether item is already in project, show add / remove button as needed
  function _check_project_item() {
    var pid = $('.project select[name=pid]').val()
    var ty = $('.project').attr('ty')
    var id = $('.project').attr('pid')

    $.ajax({
      url: '/projects/ajax/check/ty/'+ty+'/pid/'+pid+'/iid/'+id,
      type: 'GET',
      dataType: 'json',
      timeout: 5000,
      success: function(r) {
        var btns = {}
        btns[(r ? 'Remove' : 'Add')] =  function() {
          if (pid && ty && id) {
            $.ajax({
              url: '/projects/ajax/addto/pid/'+pid+'/ty/'+ty+'/iid/'+id+(r ? '/rem/1' : ''),
              type: 'GET',
              dataType: 'json',
              timeout: 5000,
              success: function(r){
                $('.project').dialog('close')
              }
            })
          }
        }
        btns['Close'] = function() { $(this).dialog('close') }

        $('.project').dialog('option', 'buttons', btns)
        $('.project').dialog('option', 'title', r ? 'Remove From Project' : 'Add To Project')
      }
    })
  }

  // Show add to project popup
  function _load_project_dialog(ty, id, name) {
    $('.project').attr('ty', ty)
    $('.project').attr('pid', id)
    $('.project span.title').html(name)

    $.ajax({
      url: '/projects/ajax/array/1',
      type: 'GET',
      dataType: 'json',
      timeout: 5000,
      success: function(r){
        $('.project select[name=pid]').empty()
        $.each(r, function(p,t) {
          $('<option value="'+p+'">'+t+'</option>').appendTo($('.project select[name=pid]'))
        })
        _check_project_item()

        $('.project').dialog('open')
      }
    })
  }

})
<commit_msg>Make project dialog stop propagation<commit_after>$(function() {
  $('button.atp').prop('title', 'Add/Remove to/from Project').button({ icons: { primary: 'ui-icon-note' }, text: false }).unbind('click').click(function() {
    _load_project_dialog($(this).attr('ty'), $(this).attr('iid'), $(this).attr('name'))
  })

  var c = $.cookie('ispyb_help')
  var help_def = ($(window).width() <= 800) ? 0 : 1
  var help = c === undefined ? help_def : c

  $('[title]').each(function(i,e) {
    $(e).attr('t_old', $(e).attr('title'))
  })

  $.editable.addInputType('autocomplete', {
      element : $.editable.types.text.element,
      plugin : function(settings, original) {
          $('input', this).autocomplete(settings.autocomplete);
      }
  })

  /*$('.current').editable('/proposal/ajax/set/', {
    type: 'autocomplete',
    autocomplete: { source: '/proposal/ajax/p/' },
    name: 'prop',
    width: '60px',
    submit: 'Ok',
    style: 'display: inline',
    placeholder: 'Click to Select',
    //onblur: 'ignore',
    callback: function() { window.location.href = '/proposal/visits' },
  }).addClass('editable');*/

  //$('#sidebar a.pull').click(function() {
  //  $('#sidebar ul').slideToggle()
  //})

  $('a.pull').addClass('enable').click(function() { $('body').toggleClass('active'); return false })

  $('#sidebar ul li.help a').click(function() {
    help = help == 1 ? 0 : 1;
    $.cookie('ispyb_help', help);
    _toggle_help()

    return false;
  })

  function _toggle_help() {
    if (help == 1) {
      $('#sidebar ul li.help').addClass('active')
      $('[title]').tipTip({delay:100, fadeIn: 400, defaultPosition: 'top'})
      $('[t_old]').each(function(i,e) { $(e).data('tt_disable', false) })
      $('p.help').fadeIn()

    } else {
      $('#sidebar ul li.help').removeClass('active')
      $('[t_old]').each(function(i,e) { $(e).data('tt_disable', true) })
      $('p.help').fadeOut()
    }
  }

  _toggle_help()

  $(document).ajaxComplete(function() {
    $('button.atp').prop('title', 'Add/Remove to/from Project').button({ icons: { primary: 'ui-icon-note' }, text: false }).unbind('click').click(function(e) {
      _load_project_dialog($(this).attr('ty'), $(this).attr('iid'), $(this).attr('name'))
      e.stopPropagation();
    })

    $('[title]').each(function(i,e) {
      $(e).attr('t_old', $(e).attr('title'))
    })
    _toggle_help()
  })

  // Add to project popup
  $('.project').dialog({ title: 'Add To Project', autoOpen: false, height: 'auto', width: 'auto' });

  $('.project select[name=pid]').change(function() { _check_project_item })
  // Check whether item is already in project, show add / remove button as needed
  function _check_project_item() {
    var pid = $('.project select[name=pid]').val()
    var ty = $('.project').attr('ty')
    var id = $('.project').attr('pid')

    $.ajax({
      url: '/projects/ajax/check/ty/'+ty+'/pid/'+pid+'/iid/'+id,
      type: 'GET',
      dataType: 'json',
      timeout: 5000,
      success: function(r) {
        var btns = {}
        btns[(r ? 'Remove' : 'Add')] =  function() {
          if (pid && ty && id) {
            $.ajax({
              url: '/projects/ajax/addto/pid/'+pid+'/ty/'+ty+'/iid/'+id+(r ? '/rem/1' : ''),
              type: 'GET',
              dataType: 'json',
              timeout: 5000,
              success: function(r){
                $('.project').dialog('close')
              }
            })
          }
        }
        btns['Close'] = function() { $(this).dialog('close') }

        $('.project').dialog('option', 'buttons', btns)
        $('.project').dialog('option', 'title', r ? 'Remove From Project' : 'Add To Project')
      }
    })
  }

  // Show add to project popup
  function _load_project_dialog(ty, id, name) {
    $('.project').attr('ty', ty)
    $('.project').attr('pid', id)
    $('.project span.title').html(name)

    $.ajax({
      url: '/projects/ajax/array/1',
      type: 'GET',
      dataType: 'json',
      timeout: 5000,
      success: function(r){
        $('.project select[name=pid]').empty()
        $.each(r, function(p,t) {
          $('<option value="'+p+'">'+t+'</option>').appendTo($('.project select[name=pid]'))
        })
        _check_project_item()

        $('.project').dialog('open')
      }
    })
  }

})
<|endoftext|>


<commit_before>/**
 * https://github.com/MarcDiethelm/terrificjs-extensions
 * Adds some sugar and enhancements to @brunschgi's excellent Terrificjs frontend framework.
 */

'use strict';

/**
 * Simplify connector channel subscription
 *
 * Because the second parameter to sandbox.subscribe() (this) often is forgotten.
 * Plus, connecting to multiple channels requires you to call subscribe for every channel.
 * @author Simon Harte <simon.harte@namics.com>
 * @param {...string} channels - Connector channels to subscribe to
 */
Tc.Module.prototype.subscribe = function subscribe(channels) {
	var i = 0,
		args = arguments,
		argLen = args.length,
		channelName
	;

	for (i; i < argLen; i++) {
		channelName = channels[i];

		this.sandbox.subscribe(channelName, this);
	}
	return true;
};

/**
 * Select elements in the module context. Usage: this.$$(selector)
 * @author Marc Diethelm <marc.diethelm@namics.com>
 * @param {string} selector
 * @returns {object} – jQuery collection
 *
 */
Tc.Module.prototype.$$ = function $$(selector) {
	return this.$ctx.find(selector);
};

/**
 * Bind methods to Terrific module context.  Usage: this.bindAll(funcName [,funcName...])
 * @author Marc Diethelm <marc.diethelm@namics.com>
 * @author Simon Harte <simon.harte@namics.com>
 * @param {...string} methods - Names of methods each as a param.
 * @return {boolean|undefined} - Returns true if binding succeeds, throws an exception otherwise.
 */
Tc.Module.prototype.bindAll = function bindAll(methods) {
	var i = 0,
		args = arguments,
		argLen = args.length,
		methodName
	;

	for (i; i < argLen; i++) {
		methodName = args[i];
		if (typeof this[methodName] === 'function') {
			this[methodName] = jQuery.proxy(this, methodName);
		}
		else {
			throw new TypeError('Tc.Module.' + this.getName() + '.' + methodName + ' is not a function');
		}
	}
	return true;
};

/**
 * Get the name of the Terrific module
 * @author Remo Brunschwiler <remo.brunschwiler@namics.com>
 * @author Mathias Hayoz <mathias.hayoz@namics.com>
 * @returns {string} – Module name
 */
Tc.Module.prototype.getName = function getName() {
	var property;
	if (!this._modName) {
		findMod: for (property in Tc.Module) {
			if (Tc.Module.hasOwnProperty(property) && property !== 'constructor' && this instanceof Tc.Module[property]) {
				this._modName = property;
				break findMod;
			}
		}
	}

	return this._modName;
};

/**
 * Simplify connector channel subscription
 *
 * Because the second parameter to sandbox.subscribe() (this) often is forgotten.
 * Plus, connecting to multiple channels requires you to call subscribe for every channel.
 * @author Simon Harte <simon.harte@namics.com>
 * @param {...string} channels - Connector channels to subscribe to
 */
Tc.Module.prototype.subscribe = function subscribe(channels) {
	var i = 0,
		args = arguments,
		argLen = args.length,
		channelName
	;

	for (i; i < argLen; i++) {
		channelName = channels[i];

		this.sandbox.subscribe(channelName, this);
	}
	return true;
};

(function () {
	var cache = {};
	/**
	 * Micro-templating for modules. Extrapolates {{= foo }} variables in strings from data.
	 * This function is a remix of
	 * - Simple JavaScript Templating – John Resig - http://ejohn.org/ - MIT Licensed
	 * - https://gist.github.com/topliceanu/1537847
	 * - http://weblog.west-wind.com/posts/2008/Oct/13/Client-Templating-with-jQuery
	 * This code incorporates a fix for single-quote usage.
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @param {string} str - Template
	 * @param {object} [data] – Optional, renders template immediately if present. Data to use as the template context for variable extrapolation.
	 * @returns {function|string} - Template function, to render template with data, or if data was supplied already the rendered template.
	 */
	Tc.Module.prototype.template = function template(str, data) {

		// Figure out if we're getting a template, or if we need to
		// load the template - and be sure to cache the result.
		var fn = !/\W/.test(str) ?
			cache[str] = cache[str] ||
				template(document.getElementById(str).innerHTML) :
			// Generate a reusable function that will serve as a template
			// generator (and which will be cached).
			/*jshint -W054, -W014 */
			new Function("obj",
					"var p=[],print=function(){p.push.apply(p,arguments);};" +
					// Introduce the data as local variables using with(){}
					"with(obj){p.push('" +
					// Convert the template into pure JavaScript
					str.replace(/[\r\t\n]/g, " ")
						.replace(/'(?=[^%]*\}\}>)/g, "\t")
						.split("'").join("\\'")
						.split("\t").join("'")
						.replace(/\{\{=(.+?)\}\}/g, "',$1,'")
						.split("{{").join("');")
						.split("}}").join("p.push('")
					+ "');}return p.join('');");
		/*jshint +W054, +W014 */
		// Provide some basic currying to the user
		return data ? fn(data) : fn;
	};
})();
<commit_msg>Remove accidental duplication of subscribe method.<commit_after>/**
 * https://github.com/MarcDiethelm/terrificjs-extensions
 * Adds some sugar and enhancements to @brunschgi's excellent Terrificjs frontend framework.
 */

'use strict';

/**
 * Select elements in the module context. Usage: this.$$(selector)
 * @author Marc Diethelm <marc.diethelm@namics.com>
 * @param {string} selector
 * @returns {object} – jQuery collection
 *
 */
Tc.Module.prototype.$$ = function $$(selector) {
	return this.$ctx.find(selector);
};

/**
 * Bind methods to Terrific module context.  Usage: this.bindAll(funcName [,funcName...])
 * @author Marc Diethelm <marc.diethelm@namics.com>
 * @author Simon Harte <simon.harte@namics.com>
 * @param {...string} methods - Names of methods each as a param.
 * @return {boolean|undefined} - Returns true if binding succeeds, throws an exception otherwise.
 */
Tc.Module.prototype.bindAll = function bindAll(methods) {
	var i = 0,
		args = arguments,
		argLen = args.length,
		methodName
	;

	for (i; i < argLen; i++) {
		methodName = args[i];
		if (typeof this[methodName] === 'function') {
			this[methodName] = jQuery.proxy(this, methodName);
		}
		else {
			throw new TypeError('Tc.Module.' + this.getName() + '.' + methodName + ' is not a function');
		}
	}
	return true;
};

/**
 * Get the name of the Terrific module
 * @author Remo Brunschwiler <remo.brunschwiler@namics.com>
 * @author Mathias Hayoz <mathias.hayoz@namics.com>
 * @returns {string} – Module name
 */
Tc.Module.prototype.getName = function getName() {
	var property;
	if (!this._modName) {
		findMod: for (property in Tc.Module) {
			if (Tc.Module.hasOwnProperty(property) && property !== 'constructor' && this instanceof Tc.Module[property]) {
				this._modName = property;
				break findMod;
			}
		}
	}

	return this._modName;
};

/**
 * Simplify connector channel subscription
 *
 * Because the second parameter to sandbox.subscribe() (this) often is forgotten.
 * Plus, connecting to multiple channels requires you to call subscribe for every channel.
 * @author Simon Harte <simon.harte@namics.com>
 * @param {...string} channels - Connector channels to subscribe to
 */
Tc.Module.prototype.subscribe = function subscribe(channels) {
	var i = 0,
		args = arguments,
		argLen = args.length,
		channelName
	;

	for (i; i < argLen; i++) {
		channelName = channels[i];

		this.sandbox.subscribe(channelName, this);
	}
	return true;
};

(function () {
	var cache = {};
	/**
	 * Micro-templating for modules. Extrapolates {{= foo }} variables in strings from data.
	 * This function is a remix of
	 * - Simple JavaScript Templating – John Resig - http://ejohn.org/ - MIT Licensed
	 * - https://gist.github.com/topliceanu/1537847
	 * - http://weblog.west-wind.com/posts/2008/Oct/13/Client-Templating-with-jQuery
	 * This code incorporates a fix for single-quote usage.
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @param {string} str - Template
	 * @param {object} [data] – Optional, renders template immediately if present. Data to use as the template context for variable extrapolation.
	 * @returns {function|string} - Template function, to render template with data, or if data was supplied already the rendered template.
	 */
	Tc.Module.prototype.template = function template(str, data) {

		// Figure out if we're getting a template, or if we need to
		// load the template - and be sure to cache the result.
		var fn = !/\W/.test(str) ?
			cache[str] = cache[str] ||
				template(document.getElementById(str).innerHTML) :
			// Generate a reusable function that will serve as a template
			// generator (and which will be cached).
			/*jshint -W054, -W014 */
			new Function("obj",
					"var p=[],print=function(){p.push.apply(p,arguments);};" +
					// Introduce the data as local variables using with(){}
					"with(obj){p.push('" +
					// Convert the template into pure JavaScript
					str.replace(/[\r\t\n]/g, " ")
						.replace(/'(?=[^%]*\}\}>)/g, "\t")
						.split("'").join("\\'")
						.split("\t").join("'")
						.replace(/\{\{=(.+?)\}\}/g, "',$1,'")
						.split("{{").join("');")
						.split("}}").join("p.push('")
					+ "');}return p.join('');");
		/*jshint +W054, +W014 */
		// Provide some basic currying to the user
		return data ? fn(data) : fn;
	};
})();
<|endoftext|>


<commit_before>/*
 * Configuration values needed in js codes
 *
 * NOTE:
 * Please use the following command to avoid accidentally committing personal changes
 * git update-index --assume-unchanged src/javascript/config.js
 *
 */

function getAppId() {
  return localStorage.getItem('config.app_id') ? localStorage.getItem('config.app_id') :
               /staging\.binary\.com/i.test(window.location.hostname) ? '1098' : '1';
}

function getSocketURL() {
    var server_url = localStorage.getItem('config.server_url'),
        bluePercent = 30;
    if(!server_url) server_url =
        (/staging\.binary\.com/i.test(window.location.hostname) ? 'www2' :
            (Math.random() * 100 > bluePercent ? 'green' : 'blue')
        ) + '.binaryws.com';
    return 'wss://' + server_url + '/websockets/v3';
}
<commit_msg>Separate blue/green percentage<commit_after>/*
 * Configuration values needed in js codes
 *
 * NOTE:
 * Please use the following command to avoid accidentally committing personal changes
 * git update-index --assume-unchanged src/javascript/config.js
 *
 */

function getAppId() {
  return localStorage.getItem('config.app_id') ? localStorage.getItem('config.app_id') :
               /staging\.binary\.com/i.test(window.location.hostname) ? '1098' : '1';
}

function getSocketURL() {
    var server_url = localStorage.getItem('config.server_url'),
        loginid = $.cookie('loginid'),
        isReal = loginid && !/^VRT/.test(loginid),
        realToGreenPercent   = 90,
        virtualToBluePercent = 90,
        randomPercent = Math.random() * 100;
    if(!server_url) server_url =
        (/staging\.binary\.com/i.test(window.location.hostname) ? 'www2' :
            (isReal ? (randomPercent < realToGreenPercent   ? 'green' : 'blue' ) :
                      (randomPercent < virtualToBluePercent ? 'blue'  : 'green'))
        ) + '.binaryws.com';
    return 'wss://' + server_url + '/websockets/v3';
}
<|endoftext|>


<commit_before>/**
 * https://github.com/MarcDiethelm/terrificjs-extensions
 * Adds some sugar and enhancements to @brunschgi's excellent Terrificjs frontend framework.
 * @file terrificjs-extensions.js
 * @license MIT
 * @copyright 2014 Marc Diethelm
 */

(function () {

	'use strict';

	/**
	 * Select elements in the module context. Usage: this.$(selector)
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @param {string} selector
	 * @returns {object} - jQuery collection
	 */
	Tc.Module.prototype.$ = function $$(selector) {
		return this.$ctx.find(selector);
	};

	/**
	* Deprecated. Use Tc.Module.prototype.$
	* Select elements in the module context. Usage: this.$$(selector)
	* @deprecated Use Tc.Module.prototype.$
	* @see Tc.Module.prototype.$
	* @author Marc Diethelm <marc.diethelm@namics.com>
	* @param {string} selector
	* @returns {object} - jQuery collection
	*/
	Tc.Module.prototype.$$ = Tc.Module.prototype.$;

	/**
	 * Bind methods to Terrific module context.  Usage: this.bindAll(funcName [,funcName...])
	 * Inspired by Underscore's bindAll. http://underscorejs.org/#bindAll
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @author Simon Harte <simon.harte@namics.com>
	 * @param {...string} methods - Names of methods each as a param.
	 * @return {module} - Returns the module instance for chaining.
	 */
	Tc.Module.prototype.bindAll = function bindAll(methods) {
		var i = 0,
			args = arguments,
			argLen = args.length,
			methodName
		;

		for (i; i < argLen; i++) {
			methodName = args[i];
			if (typeof this[methodName] === 'function') {
				this[methodName] = jQuery.proxy(this, methodName);
			} else if (typeof methodName === 'string') {
				throw new TypeError('bindAll: Tc.Module.' + this.getName() + '.' + methodName + ' is not a function');
			} else {
				throw new TypeError('Arguments to bindAll must be strings');
			}
		}

		return this;
	};

	/**
	 * Get the name of the Terrific module
	 * @author Remo Brunschwiler <remo.brunschwiler@namics.com>
	 * @author Mathias Hayoz <mathias.hayoz@namics.com>
	 * @returns {string} - Module name
	 */
	Tc.Module.prototype.getName = function getName() {
		var property;
		if (!this._modName) {
			for (property in Tc.Module) {
				if (Tc.Module.hasOwnProperty(property) && property !== 'constructor' && this instanceof Tc.Module[property]) {
					this._modName = property;
					break;
				}
			}
		}

		return this._modName;
	};

	/**
	 * Simplify connector channel subscription
	 *
	 * Because the second parameter to sandbox.subscribe `this` is often forgotten.
	 * Additionally this method allows connecting to multiple channels at once.
	 * @author Simon Harte <simon.harte@namics.com>
	 * @param {...string} channels - Connector channels to subscribe to
	 * @return {module} - Returns the module instance for chaining.
	 */
	Tc.Module.prototype.subscribe = function subscribe(channels) {
		var i = 0,
			args = arguments,
			argLen = args.length
		;

		for (i; i < argLen; i++) {
			this.sandbox.subscribe(args[i], this);
		}

		return this;
	};

	/**
	 * Get all channels the module is currently subscribed to
	 *
	 * @author Simon Harte <simon.harte@namics.com>
	 * @return {object, string} channels - Returns the array of connected channels
	 */
	Tc.Module.prototype.getConnectors = function getConnectors() {
		var channels = [],
			chan,
			curr;

		for (chan in this.connectors) {
			if (this.connectors.hasOwnProperty(chan)) {
				curr = this.connectors[chan];
				channels.push(curr.connectorId);
			}
		}

		return channels;
	};

	/**
	 * Fire on all channels the current module is subscribed to
	 *
	 * @author Simon Harte <simon.harte@namics.com>
	 * @param {string} method - Function to execute in connected modules
	 * @param {object} [data] - The data to provide to your connected modules (optional)
	 * @return {object} this - Returns the module instance for chaining
	 */
	Tc.Module.prototype.fireAll = function fireAll(method, data) {
		var channels = this.getConnectors();

		this.fire(method, data || {}, channels);

		return this;
	};

	var tplCache = {};
	/**
	 * Micro-templating for modules. Extrapolates {{= foo }} variables in strings from data.
	 * This function is a remix of
	 * - Simple JavaScript Templating – John Resig - http://ejohn.org/ - MIT Licensed
	 * - https://gist.github.com/topliceanu/1537847
	 * - http://weblog.west-wind.com/posts/2008/Oct/13/Client-Templating-with-jQuery
	 * This code incorporates a fix for single-quote usage.
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @param {string} str - Template
	 * @param {object} [data] - Optional, renders template immediately if present. Data to use as the template context for variable extrapolation.
	 * @returns {function|string} - Template function, to render template with data, or if data was supplied already the rendered template.
	 */
	Tc.Module.prototype.template = function template(str, data) {

		// Figure out if we're getting a template, or if we need to
		// load the template - and be sure to cache the result.
		var fn = !/\W/.test(str) ?
			tplCache[str] = tplCache[str] ||
				template(document.getElementById(str).innerHTML) :
			// Generate a reusable function that will serve as a template
			// generator (and which will be cached).
			/*jshint -W054, -W014 */
			new Function("obj",
					"var p=[],print=function(){p.push.apply(p,arguments);};" +
					// Introduce the data as local variables using with(){}
					"with(obj){p.push('" +
					// Convert the template into pure JavaScript
					str.replace(/[\r\t\n]/g, " ")
						.replace(/'(?=[^%]*\}\}>)/g, "\t")
						.split("'").join("\\'")
						.split("\t").join("'")
						.replace(/\{\{=(.+?)\}\}/g, "',$1,'")
						.split("{{").join("');")
						.split("}}").join("p.push('")
					+ "');}return p.join('');");
		/*jshint +W054, +W014 */
		// Provide some basic currying to the user
		return data ? fn(data) : fn;
	};

})();
<commit_msg>pass in jQuery or zepto<commit_after>/**
 * https://github.com/MarcDiethelm/terrificjs-extensions
 * Adds some sugar and enhancements to @brunschgi's excellent Terrificjs frontend framework.
 * @file terrificjs-extensions.js
 * @license MIT
 * @copyright 2014 Marc Diethelm
 */

(function ($) {

	'use strict';

	/**
	 * Select elements in the module context. Usage: this.$(selector)
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @param {string} selector
	 * @returns {object} - jQuery collection
	 */
	Tc.Module.prototype.$ = function $$(selector) {
		return this.$ctx.find(selector);
	};

	/**
	* Deprecated. Use Tc.Module.prototype.$
	* Select elements in the module context. Usage: this.$$(selector)
	* @deprecated Use Tc.Module.prototype.$
	* @see Tc.Module.prototype.$
	* @author Marc Diethelm <marc.diethelm@namics.com>
	* @param {string} selector
	* @returns {object} - jQuery collection
	*/
	Tc.Module.prototype.$$ = Tc.Module.prototype.$;

	/**
	 * Bind methods to Terrific module context.  Usage: this.bindAll(funcName [,funcName...])
	 * Inspired by Underscore's bindAll. http://underscorejs.org/#bindAll
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @author Simon Harte <simon.harte@namics.com>
	 * @param {...string} methods - Names of methods each as a param.
	 * @return {module} - Returns the module instance for chaining.
	 */
	Tc.Module.prototype.bindAll = function bindAll(methods) {
		var i = 0,
			args = arguments,
			argLen = args.length,
			methodName
		;

		for (i; i < argLen; i++) {
			methodName = args[i];
			if (typeof this[methodName] === 'function') {
				this[methodName] = $.proxy(this, methodName);
			} else if (typeof methodName === 'string') {
				throw new TypeError('bindAll: Tc.Module.' + this.getName() + '.' + methodName + ' is not a function');
			} else {
				throw new TypeError('Arguments to bindAll must be strings');
			}
		}

		return this;
	};

	/**
	 * Get the name of the Terrific module
	 * @author Remo Brunschwiler <remo.brunschwiler@namics.com>
	 * @author Mathias Hayoz <mathias.hayoz@namics.com>
	 * @returns {string} - Module name
	 */
	Tc.Module.prototype.getName = function getName() {
		var property;
		if (!this._modName) {
			for (property in Tc.Module) {
				if (Tc.Module.hasOwnProperty(property) && property !== 'constructor' && this instanceof Tc.Module[property]) {
					this._modName = property;
					break;
				}
			}
		}

		return this._modName;
	};

	/**
	 * Simplify connector channel subscription
	 *
	 * Because the second parameter to sandbox.subscribe `this` is often forgotten.
	 * Additionally this method allows connecting to multiple channels at once.
	 * @author Simon Harte <simon.harte@namics.com>
	 * @param {...string} channels - Connector channels to subscribe to
	 * @return {module} - Returns the module instance for chaining.
	 */
	Tc.Module.prototype.subscribe = function subscribe(channels) {
		var i = 0,
			args = arguments,
			argLen = args.length
		;

		for (i; i < argLen; i++) {
			this.sandbox.subscribe(args[i], this);
		}

		return this;
	};

	/**
	 * Get all channels the module is currently subscribed to
	 *
	 * @author Simon Harte <simon.harte@namics.com>
	 * @return {object, string} channels - Returns the array of connected channels
	 */
	Tc.Module.prototype.getConnectors = function getConnectors() {
		var channels = [],
			chan,
			curr;

		for (chan in this.connectors) {
			if (this.connectors.hasOwnProperty(chan)) {
				curr = this.connectors[chan];
				channels.push(curr.connectorId);
			}
		}

		return channels;
	};

	/**
	 * Fire on all channels the current module is subscribed to
	 *
	 * @author Simon Harte <simon.harte@namics.com>
	 * @param {string} method - Function to execute in connected modules
	 * @param {object} [data] - The data to provide to your connected modules (optional)
	 * @return {object} this - Returns the module instance for chaining
	 */
	Tc.Module.prototype.fireAll = function fireAll(method, data) {
		var channels = this.getConnectors();

		this.fire(method, data || {}, channels);

		return this;
	};

	var tplCache = {};
	/**
	 * Micro-templating for modules. Extrapolates {{= foo }} variables in strings from data.
	 * This function is a remix of
	 * - Simple JavaScript Templating – John Resig - http://ejohn.org/ - MIT Licensed
	 * - https://gist.github.com/topliceanu/1537847
	 * - http://weblog.west-wind.com/posts/2008/Oct/13/Client-Templating-with-jQuery
	 * This code incorporates a fix for single-quote usage.
	 * @author Marc Diethelm <marc.diethelm@namics.com>
	 * @param {string} str - Template
	 * @param {object} [data] - Optional, renders template immediately if present. Data to use as the template context for variable extrapolation.
	 * @returns {function|string} - Template function, to render template with data, or if data was supplied already the rendered template.
	 */
	Tc.Module.prototype.template = function template(str, data) {

		// Figure out if we're getting a template, or if we need to
		// load the template - and be sure to cache the result.
		var fn = !/\W/.test(str) ?
			tplCache[str] = tplCache[str] ||
				template(document.getElementById(str).innerHTML) :
			// Generate a reusable function that will serve as a template
			// generator (and which will be cached).
			/*jshint -W054, -W014 */
			new Function("obj",
					"var p=[],print=function(){p.push.apply(p,arguments);};" +
					// Introduce the data as local variables using with(){}
					"with(obj){p.push('" +
					// Convert the template into pure JavaScript
					str.replace(/[\r\t\n]/g, " ")
						.replace(/'(?=[^%]*\}\}>)/g, "\t")
						.split("'").join("\\'")
						.split("\t").join("'")
						.replace(/\{\{=(.+?)\}\}/g, "',$1,'")
						.split("{{").join("');")
						.split("}}").join("p.push('")
					+ "');}return p.join('');");
		/*jshint +W054, +W014 */
		// Provide some basic currying to the user
		return data ? fn(data) : fn;
	};

})($ || jQuery);
<|endoftext|>