<commit_before>const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
          id: action.id,
          text: action.text,
          completed: false
        };

    case 'TOGGLE_TODO':
        if (state.id !== action.id) {
          return state;
        }
        return {
          ...state,
          completed: !state.completed
        };

    default:
      return state;
  }
};

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        todo(undefined, action)
      ];

    case 'TOGGLE_TODO':
      return state.map(t => todo(t, action));

    default:
      return state;
  }
};

const visibilityFilter = (state = 'SHOW_ALL', action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default:
      return state;
  }
};

const todoApp = (state = {}, action) => {
  return {
    todos: todos(
      state.todos,
      action
    ),
    visibilityFilter: visibilityFilter(
      state.visibilityFilter,
      action
    )
  };
};

// testing
const testAddTodo = () => {
  const stateBefore = [];
  const action = {
    type: 'ADD_TODO',
    id: 0,
    text: 'Learn Redux'
  };
  const stateAfter = [
    {
      id: 0,
      text: 'Learn Redux',
      completed: false
    }
  ];

  deepFreeze(stateBefore);
  deepFreeze(action);

  expect(
    todos(stateBefore, action)
  ).toEqual(stateAfter);
};

const testToggleTodo = () => {
  const stateBefore = [
    {
      id: 0,
      text: 'Learn Redux',
      completed: false
    },
    {
      id: 1,
      text: 'Go shopping',
      completed: false
    }
  ];

  const action = {
    type: 'TOGGLE_TODO',
    id: 1
  };

  const stateAfter = [
    {
      id: 0,
      text: 'Learn Redux',
      completed: false
    },
    {
      id: 1,
      text: 'Go shopping',
      completed: true
    }
  ];

  deepFreeze(stateBefore);
  deepFreeze(action);

  expect(
    todos(stateBefore, action)
  ).toEqual(stateAfter);

};

const testTodoApp = () => {
  const stateBefore = {}
  const action = {
    type: 'ADD_TODO',
    text: 'master csgo',
    id: 3
  };
  const stateAfter = {
    todos: [{
      id: 3,
      text: 'master csgo',
      completed: false
    }],
    visibilityFilter: 'SHOW_ALL'
  };

  deepFreeze(stateBefore);
  deepFreeze(action);

  expect(
    todoApp(stateBefore, action)
  ).toEqual(stateAfter);

}

testAddTodo();
testToggleTodo();
testTodoApp();
console.log('All tests passed');

const { createStore} = Redux;
const store = createStore(todoApp);

console.log('Initial state: ');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching ADD_TODO');
store.dispatch({
  type: 'ADD_TODO',
  id: 0,
  text: 'Learn Redux'
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching ADD_TODO');
store.dispatch({
  type: 'ADD_TODO',
  id: 1,
  text: 'Go Shopping'
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching TOGGLE_TODO');
store.dispatch({
  type: 'TOGGLE_TODO',
  id: 1
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching SET_VISIBILITY_FILTER');
store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');<commit_msg>using combineReducers()<commit_after>const todo = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
          id: action.id,
          text: action.text,
          completed: false
        };

    case 'TOGGLE_TODO':
        if (state.id !== action.id) {
          return state;
        }
        return {
          ...state,
          completed: !state.completed
        };

    default:
      return state;
  }
};

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        todo(undefined, action)
      ];

    case 'TOGGLE_TODO':
      return state.map(t => todo(t, action));

    default:
      return state;
  }
};

const visibilityFilter = (state = 'SHOW_ALL', action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default:
      return state;
  }
};

const { combineReducers } = Redux;

const todoApp = combineReducers({
  todos,
  visibilityFilter
});

// const todoApp = combineReducers({
//   todos: todos,
//   visibilityFilter: visibilityFilter
// });

// const todoApp = (state = {}, action) => {
//   return {
//     todos: todos(
//       state.todos,
//       action
//     ),
//     visibilityFilter: visibilityFilter(
//       state.visibilityFilter,
//       action
//     )
//   };
// };

// testing
const testAddTodo = () => {
  const stateBefore = [];
  const action = {
    type: 'ADD_TODO',
    id: 0,
    text: 'Learn Redux'
  };
  const stateAfter = [
    {
      id: 0,
      text: 'Learn Redux',
      completed: false
    }
  ];

  deepFreeze(stateBefore);
  deepFreeze(action);

  expect(
    todos(stateBefore, action)
  ).toEqual(stateAfter);
};

const testToggleTodo = () => {
  const stateBefore = [
    {
      id: 0,
      text: 'Learn Redux',
      completed: false
    },
    {
      id: 1,
      text: 'Go shopping',
      completed: false
    }
  ];

  const action = {
    type: 'TOGGLE_TODO',
    id: 1
  };

  const stateAfter = [
    {
      id: 0,
      text: 'Learn Redux',
      completed: false
    },
    {
      id: 1,
      text: 'Go shopping',
      completed: true
    }
  ];

  deepFreeze(stateBefore);
  deepFreeze(action);

  expect(
    todos(stateBefore, action)
  ).toEqual(stateAfter);

};

const testTodoApp = () => {
  const stateBefore = {}
  const action = {
    type: 'ADD_TODO',
    text: 'master csgo',
    id: 3
  };
  const stateAfter = {
    todos: [{
      id: 3,
      text: 'master csgo',
      completed: false
    }],
    visibilityFilter: 'SHOW_ALL'
  };

  deepFreeze(stateBefore);
  deepFreeze(action);

  expect(
    todoApp(stateBefore, action)
  ).toEqual(stateAfter);

}

testAddTodo();
testToggleTodo();
testTodoApp();
console.log('All tests passed');

const { createStore} = Redux;
const store = createStore(todoApp);

console.log('Initial state: ');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching ADD_TODO');
store.dispatch({
  type: 'ADD_TODO',
  id: 0,
  text: 'Learn Redux'
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching ADD_TODO');
store.dispatch({
  type: 'ADD_TODO',
  id: 1,
  text: 'Go Shopping'
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching TOGGLE_TODO');
store.dispatch({
  type: 'TOGGLE_TODO',
  id: 1
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');

console.log('Dispatching SET_VISIBILITY_FILTER');
store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
});

console.log('Current state:');
console.log(store.getState());
console.log('---------------');<|endoftext|>


<commit_before>/**!
 *
 * Copyright (c) 2015-2016 Cisco Systems, Inc. See LICENSE file.
 * @private
 */

import {map} from 'lodash';
import {SparkPlugin} from '@ciscospark/spark-core';

const Flag = SparkPlugin.extend({
  namespace: `Flag`,

  session: {
    connected: {
      default: false,
      type: `boolean`
    },
    connecting: {
      default: false,
      type: `boolean`
    },
    socket: `object`
  },

  derived: {
    listening: {
      deps: [`connected`],
      fn() {
        return this.connected;
      }
    }
  },

  /**
  * Archive a flag
  * @param {Object} flag
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag archival
  */
  archive(flag, options) {
    if (!flag.id) {
      return Promise.reject(new Error(`\`flag.id\` is required`));
    }
    options = options || {};
    const params = {
      method: `PUT`,
      uri: flag.url,
      options,
      body: {
        state: `archived`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body);
  },

  /**
  * Flags an activity
  * @param {Object} activity
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag creation
  */
  flag(activity, options) {
    if (!activity.url) {
      return Promise.reject(new Error(`\`activity.url\` is required`));
    }
    options = options || {};
    const params = {
      method: `POST`,
      api: `userApps`,
      resource: `/flags`,
      options,
      body: {
        'flag-item': activity.url,
        state: `flagged`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body);
  },

  /**
  * Gets a list of Flags for a user
  * @param {Object} options
  * @returns {Promise} Resolves with the fetched flags
  */
  list(options) {
    options = options || {};
    const params = {
      method: `GET`,
      api: `userApps`,
      resource: `/flags`,
      options,
      qs: {
        state: `flagged`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body.items);
  },

  /**
  * Gets an array of activities where the status is 200
  * @param {Object} flags
  * @returns {Promise<Object>} Resolves with the activities
  * TODO: this should be implemented as a batched request when migrating to the modular sdk
  */
  mapToActivities(flags) {
    const activityUrls = map(flags, `flag-item`);

    const params = {
      method: `POST`,
      api: `conversation`,
      resource: `bulk_activities_fetch`,
      body: {
        activityUrls
      }
    };

    return this.spark.request(params)
      .then((res) => {
        const activitiesArr = [];
        res.body.multistatus.forEach((statusData) => {
          if (statusData.status === `200`) {
            activitiesArr.push(statusData.data.activity);
          }
        });
        return activitiesArr;
      });
  },

  /**
  * Delete a flag
  * @param {Object} flag
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag deletion
  */
  remove(flag, options) {
    if (!flag.id) {
      return Promise.reject(new Error(`\`flag.id\` is required`));
    }
    options = options || {};
    const params = {
      method: `DELETE`,
      options,
      uri: flag.url
    };

    return this.request(params)
      .then((res) => res.body);
  },

  /**
  * UnFlags an activity
  * @param {Object} flag
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag removal
  */
  unflag(flag, options) {
    if (!flag.id) {
      return Promise.reject(new Error(`\`flag.id\` is required`));
    }
    options = options || {};
    const params = {
      method: `PUT`,
      uri: flag.url,
      options,
      body: {
        state: `unflagged`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body);
  }

});

export default Flag;
<commit_msg>review(flag-plugin): review comments incorporated<commit_after>/**!
 *
 * Copyright (c) 2015-2016 Cisco Systems, Inc. See LICENSE file.
 * @private
 */

import {map} from 'lodash';
import {SparkPlugin} from '@ciscospark/spark-core';

const Flag = SparkPlugin.extend({
  namespace: `Flag`,

  /**
  * Archive a flag
  * @param {Object} flag
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag archival
  */
  archive(flag, options) {
    if (!flag.id) {
      return Promise.reject(new Error(`\`flag.id\` is required`));
    }
    options = options || {};
    const params = {
      method: `PUT`,
      uri: flag.url,
      options,
      body: {
        state: `archived`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body);
  },

  /**
  * Flags an activity
  * @param {Object} activity
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag creation
  */
  flag(activity, options) {
    if (!activity.url) {
      return Promise.reject(new Error(`\`activity.url\` is required`));
    }
    options = options || {};
    const params = {
      method: `POST`,
      api: `userApps`,
      resource: `/flags`,
      options,
      body: {
        'flag-item': activity.url,
        state: `flagged`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body);
  },

  /**
  * Gets a list of Flags for a user
  * @param {Object} options
  * @returns {Promise} Resolves with the fetched flags
  */
  list(options) {
    options = options || {};
    const params = {
      method: `GET`,
      api: `userApps`,
      resource: `/flags`,
      options,
      qs: {
        state: `flagged`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body.items);
  },

  /**
  * Gets an array of activities where the status is 200
  * @param {Object} flags
  * @returns {Promise<Object>} Resolves with the activities
  * TODO: this should be implemented as a batched request when migrating to the modular sdk
  */
  mapToActivities(flags) {
    const activityUrls = map(flags, `flag-item`);

    const params = {
      method: `POST`,
      api: `conversation`,
      resource: `bulk_activities_fetch`,
      body: {
        activityUrls
      }
    };

    return this.spark.request(params)
      .then((res) => {
        const activitiesArr = [];
        res.body.multistatus.forEach((statusData) => {
          if (statusData.status === `200`) {
            activitiesArr.push(statusData.data.activity);
          }
        });
        return activitiesArr;
      });
  },

  /**
  * Delete a flag
  * @param {Object} flag
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag deletion
  */
  remove(flag, options) {
    if (!flag.id) {
      return Promise.reject(new Error(`\`flag.id\` is required`));
    }
    options = options || {};
    const params = {
      method: `DELETE`,
      options,
      uri: flag.url
    };

    return this.request(params)
      .then((res) => res.body);
  },

  /**
  * UnFlags an activity
  * @param {Object} flag
  * @param {Object} options
  * @returns {Promise<Object>} Resolves with the flag removal
  */
  unflag(flag, options) {
    if (!flag.id) {
      return Promise.reject(new Error(`\`flag.id\` is required`));
    }
    options = options || {};
    const params = {
      method: `PUT`,
      uri: flag.url,
      options,
      body: {
        state: `unflagged`
      }
    };

    return this.spark.request(params)
      .then((res) => res.body);
  }

});

export default Flag;
<|endoftext|>


<commit_before>var expect = require('chai').expect,
  Snapshot = require('../../../../web/client/models/snapshot')

describe('snapshot', function () {
  it('should return formatted size', function () {
    var snapshot = new Snapshot({
      size: 5
    })

    expect(snapshot.sizeFormatted).to.equal('5 Bytes')
  })

  it('should return formatted date', function () {
    var snapshot = new Snapshot({
      date: 10
    })

    expect(snapshot.dateFormatted).to.equal('1970-01-01 01:00:00 +01:00')
  })
})
<commit_msg>fix failing test by ignoring time zone differences<commit_after>var expect = require('chai').expect,
  Snapshot = require('../../../../web/client/models/snapshot')

describe('snapshot', function () {
  it('should return formatted size', function () {
    var snapshot = new Snapshot({
      size: 5
    })

    expect(snapshot.sizeFormatted).to.equal('5 Bytes')
  })

  it('should return formatted date', function () {
    var snapshot = new Snapshot({
      date: 10
    })

    // ignore timezone offset..
    expect(snapshot.dateFormatted).to.contain('1970-01-01 01:00:00')
  })
})
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.GAShim = factory();
    }
}(this, function () {

    var GAShim = {};

    /**
     * Maps old GA commands to it's respective GUA equivalent
     *
     * apiName - name of new API in GUA that corresponds with the
     *           key name.
     * args    - Defines the name of the arguments that will be used to
     *           construct the argument map to be passed into the GUA API.
     *           The ordering also corresponds to the order of these fields
     *           in the old GA API.
     * send    - Send should be set to 'true' if the first argument passed
     *           into the GUA API is 'send'. We differentiate on this because
     *           the API is called slightly differently for this case.
     *
     * For commands that aren't simple to convert, you can define a function that
     * will convert into the format needed for the `ga` GUA API. The function
     * provides the arguments of the old API, and expects exact arguments
     * that should be applied to `ga` in return.
     */
    GAShim._gaCommandShimMap = {
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
        '_addTrans': {
            'apiName': 'ecommerce:addTransaction',
            'args': ['id', 'affiliation', 'revenue', 'tax', 'shipping']
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
        '_addItem': {
            'apiName': 'ecommerce:addItem',
            'args': ['id', 'sku', 'name', 'category', 'price', 'quantity']
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
        '_trackTrans': {
            'apiName': 'ecommerce:send'
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
        //      https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEventTracking#_gat.GA_EventTracker_._trackEvent
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/events
        '_trackEvent': {
            'send': true,
            'apiName': 'event',
            'args': ['eventCategory', 'eventAction', 'eventLabel', 'eventValue', 'nonInteraction']
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_gat.GA_Tracker_._trackPageview
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
        '_trackPageview': {
            'send': true,
            'apiName': 'pageview',
            'args': ['page']
        },
        // GA: https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
        '_setCustomVar': function(args, trackerNamespace) {
            // Args 1 (name) and 3 (scope) that were required in the old GA API
            // are not required in GUA because these vars must be set in the
            // admin interface of Google Analytics.
            // This function must return the arguments passed to the `ga` GUA API
            // in an array.
            var apiName = trackerNamespace + 'set';
            var dimension = 'dimension' + args[0];
            var value = args[2];
            return [apiName, dimension, value];
        }
    };

    GAShim._isArray = function(someVar) {
        return Object.prototype.toString.call(someVar) === '[object Array]';
    };

    GAShim._isFunction = function(someVar) {
        return Object.prototype.toString.call(someVar) === '[object Function]';
    };

    /**
     * Given a particular GA commandArray, convert it into an array that we can apply
     * to the new GUA API.
     *
     * commandArray     - The GA command being shimmed. Name from:
     *                    https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gaq#_gaq.push
     * trackerNamespace - (optional) The custom tracker name on the GUA object
     */
    GAShim.convertGACommandToNewGUAFormat = function(commandArray, trackerNamespace) {
        trackerNamespace = trackerNamespace ? trackerNamespace + '.' : '';
        // The name of the command in the classic GA API is contained
        // in the first argument of the commandArray.
        var commandName = commandArray[0];

        // The arguments of the command in the classic GA API are
        // contained in the 1..n elements of the commandArray.
        var commandArgs = commandArray.slice(1);

        // Identify which new command this maps to.
        var commandMapObj = GAShim._gaCommandShimMap[commandName];
        if (!commandMapObj) {
            return;
        }

        // If there are arguments, attempt to create the key-value
        // arguments object needed for the corresponding GUA command.
        if (commandMapObj['args']) {
            var newArgs = {};

            for (var i=0, argsLen=commandArgs.length; i<argsLen; i++) {
                var argId = commandMapObj['args'][i];
                if (!argId) {
                    continue;
                }
                // if commandArg is undefined or null (which it can be for
                // optional arguments), don't set it.
                var commandArg = commandArgs[i];
                if (!commandArg) {
                    continue;
                }
                newArgs[argId] = commandArg;
            }

            // Call ga using the new command name and corresponding arguments
            // If 'send' is true, this means this type of API has a special syntax
            // where the first argument is 'trackerNamespace.send' or 'send', rather then
            // the event name directly, and the name of the event will actually
            // be stored in the newArgs object as 'hitType'
            if (commandMapObj.send) {
                newArgs['hitType'] = commandMapObj['apiName'];
                return [trackerNamespace + 'send', newArgs];
            } else {
                return [trackerNamespace + commandMapObj['apiName'], newArgs];
            }
        // If commandMapObj is a function, then the arguments to `ga` will be
        // constructed from that command, which must return an array when executed.
        } else if (GAShim._isFunction(commandMapObj)) {
            return commandMapObj.call(null, commandArgs, trackerNamespace);
        // If there are no arguments, simply execute without any
        } else {
            return [trackerNamespace + commandMapObj['apiName']];
        }
    };

    /**
     * Calls the shim on all existing events in the queue, and overrides
     * the old gaq to push events to the new GUA object
     */
    GAShim.bindShim = function(gaq, ga, trackerNamespace) {
        // Grab all commandArrays on the old queue and shim them
        if (gaq.length > 0) {
            for (var i=0, len=gaq.length; i<len; i++) {
                var newCommandArray = GAShim.convertGACommandToNewGUAFormat(gaq[i], trackerNamespace);
                ga.apply(window, newCommandArray);
            }
        }

        // Shim all future calls to `gaq.push`
        gaq.push = function() {
            for (var i=0, len=arguments.length; i<len; i++) {
                // Attempt to shim the command array
                var commandArray = arguments[i];
                try {
                    var newCommandArray = GAShim.convertGACommandToNewGUAFormat(commandArray, trackerNamespace);
                    ga.apply(window, newCommandArray);
                } catch (e) {
                    throw e
                    console.error('There was an issue with shimming the following command: ' + commandArray[i], e);
                }
            }
        };
    };

    return GAShim;

}));<commit_msg>Add a copyright header<commit_after>/**
 * ga-shim.js 0.0.1
 *
 * (c) 2014 Shawn Jansepar, Mobify
 * ga-shim.js may be freely distributed under the MIT license.
 *
 */
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.GAShim = factory();
    }
}(this, function () {

    var GAShim = {};

    /**
     * Maps old GA commands to it's respective GUA equivalent
     *
     * apiName - name of new API in GUA that corresponds with the
     *           key name.
     * args    - Defines the name of the arguments that will be used to
     *           construct the argument map to be passed into the GUA API.
     *           The ordering also corresponds to the order of these fields
     *           in the old GA API.
     * send    - Send should be set to 'true' if the first argument passed
     *           into the GUA API is 'send'. We differentiate on this because
     *           the API is called slightly differently for this case.
     *
     * For commands that aren't simple to convert, you can define a function that
     * will convert into the format needed for the `ga` GUA API. The function
     * provides the arguments of the old API, and expects exact arguments
     * that should be applied to `ga` in return.
     */
    GAShim._gaCommandShimMap = {
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
        '_addTrans': {
            'apiName': 'ecommerce:addTransaction',
            'args': ['id', 'affiliation', 'revenue', 'tax', 'shipping']
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
        '_addItem': {
            'apiName': 'ecommerce:addItem',
            'args': ['id', 'sku', 'name', 'category', 'price', 'quantity']
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
        '_trackTrans': {
            'apiName': 'ecommerce:send'
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
        //      https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEventTracking#_gat.GA_EventTracker_._trackEvent
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/events
        '_trackEvent': {
            'send': true,
            'apiName': 'event',
            'args': ['eventCategory', 'eventAction', 'eventLabel', 'eventValue', 'nonInteraction']
        },
        // GA:  https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiBasicConfiguration#_gat.GA_Tracker_._trackPageview
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
        '_trackPageview': {
            'send': true,
            'apiName': 'pageview',
            'args': ['page']
        },
        // GA: https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables
        // GUA: https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
        '_setCustomVar': function(args, trackerNamespace) {
            // Args 1 (name) and 3 (scope) that were required in the old GA API
            // are not required in GUA because these vars must be set in the
            // admin interface of Google Analytics.
            // This function must return the arguments passed to the `ga` GUA API
            // in an array.
            var apiName = trackerNamespace + 'set';
            var dimension = 'dimension' + args[0];
            var value = args[2];
            return [apiName, dimension, value];
        }
    };

    GAShim._isArray = function(someVar) {
        return Object.prototype.toString.call(someVar) === '[object Array]';
    };

    GAShim._isFunction = function(someVar) {
        return Object.prototype.toString.call(someVar) === '[object Function]';
    };

    /**
     * Given a particular GA commandArray, convert it into an array that we can apply
     * to the new GUA API.
     *
     * commandArray     - The GA command being shimmed. Name from:
     *                    https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gaq#_gaq.push
     * trackerNamespace - (optional) The custom tracker name on the GUA object
     */
    GAShim.convertGACommandToNewGUAFormat = function(commandArray, trackerNamespace) {
        trackerNamespace = trackerNamespace ? trackerNamespace + '.' : '';
        // The name of the command in the classic GA API is contained
        // in the first argument of the commandArray.
        var commandName = commandArray[0];

        // The arguments of the command in the classic GA API are
        // contained in the 1..n elements of the commandArray.
        var commandArgs = commandArray.slice(1);

        // Identify which new command this maps to.
        var commandMapObj = GAShim._gaCommandShimMap[commandName];
        if (!commandMapObj) {
            return;
        }

        // If there are arguments, attempt to create the key-value
        // arguments object needed for the corresponding GUA command.
        if (commandMapObj['args']) {
            var newArgs = {};

            for (var i=0, argsLen=commandArgs.length; i<argsLen; i++) {
                var argId = commandMapObj['args'][i];
                if (!argId) {
                    continue;
                }
                // if commandArg is undefined or null (which it can be for
                // optional arguments), don't set it.
                var commandArg = commandArgs[i];
                if (!commandArg) {
                    continue;
                }
                newArgs[argId] = commandArg;
            }

            // Call ga using the new command name and corresponding arguments
            // If 'send' is true, this means this type of API has a special syntax
            // where the first argument is 'trackerNamespace.send' or 'send', rather then
            // the event name directly, and the name of the event will actually
            // be stored in the newArgs object as 'hitType'
            if (commandMapObj.send) {
                newArgs['hitType'] = commandMapObj['apiName'];
                return [trackerNamespace + 'send', newArgs];
            } else {
                return [trackerNamespace + commandMapObj['apiName'], newArgs];
            }
        // If commandMapObj is a function, then the arguments to `ga` will be
        // constructed from that command, which must return an array when executed.
        } else if (GAShim._isFunction(commandMapObj)) {
            return commandMapObj.call(null, commandArgs, trackerNamespace);
        // If there are no arguments, simply execute without any
        } else {
            return [trackerNamespace + commandMapObj['apiName']];
        }
    };

    /**
     * Calls the shim on all existing events in the queue, and overrides
     * the old gaq to push events to the new GUA object
     */
    GAShim.bindShim = function(gaq, ga, trackerNamespace) {
        // Grab all commandArrays on the old queue and shim them
        if (gaq.length > 0) {
            for (var i=0, len=gaq.length; i<len; i++) {
                var newCommandArray = GAShim.convertGACommandToNewGUAFormat(gaq[i], trackerNamespace);
                ga.apply(window, newCommandArray);
            }
        }

        // Shim all future calls to `gaq.push`
        gaq.push = function() {
            for (var i=0, len=arguments.length; i<len; i++) {
                // Attempt to shim the command array
                var commandArray = arguments[i];
                try {
                    var newCommandArray = GAShim.convertGACommandToNewGUAFormat(commandArray, trackerNamespace);
                    ga.apply(window, newCommandArray);
                } catch (e) {
                    throw e
                    console.error('There was an issue with shimming the following command: ' + commandArray[i], e);
                }
            }
        };
    };

    return GAShim;

}));
<|endoftext|>


<commit_before>/* globals AFRAME */
var Events = require('./Events');
var shouldCaptureKeyEvent = AFRAME.utils.shouldCaptureKeyEvent;
import {removeSelectedEntity, cloneSelectedEntity} from '../actions/entity';

module.exports = {
  onKeyUp: function (event) {
    if (!shouldCaptureKeyEvent(event)) { return; }

    // h: help
    if (event.keyCode === 72) {
      Events.emit('openhelpmodal');
    }

    // esc: close inspector
    if (event.keyCode === 27) {
      AFRAME.INSPECTOR.close();
      return;
    }

    // w: translate
    if (event.keyCode === 87) {
      Events.emit('transformmodechanged', 'translate');
    }

    // e: rotate
    if (event.keyCode === 69) {
      Events.emit('transformmodechanged', 'rotate');
    }

    // r: scale
    if (event.keyCode === 82) {
      Events.emit('transformmodechanged', 'scale');
    }

    // g: toggle grid
    if (event.keyCode === 71) {
      Events.emit('togglegrid');
    }

    // n: new entity
    if (event.keyCode === 78) {
      Events.emit('createnewentity', {element: 'a-entity', components: {}});
    }

    // backspace & supr: remove selected entity
    if (event.keyCode === 8 || event.keyCode === 46) {
      removeSelectedEntity();
    }

    // d: clone selected entity
    if (event.keyCode === 68) {
      cloneSelectedEntity();
    }
  },
  enable: function () {
    window.addEventListener('keyup', this.onKeyUp, false);
  },
  disable: function () {
    window.removeEventListener('keyup', this.onKeyUp);
  }
};
<commit_msg>Do not fire hotkey shortcuts dependent on activeElement, but instead allow events if not originating on an input element<commit_after>/* globals AFRAME */
var Events = require('./Events');
import {removeSelectedEntity, cloneSelectedEntity} from '../actions/entity';

function shouldCaptureKeyEvent (event) {
  if (event.metaKey) { return false; }
  return event.target.tagName !== 'INPUT' &&
    event.target.tagName !== 'TEXTAREA';
}

module.exports = {
  onKeyUp: function (event) {
    if (!shouldCaptureKeyEvent(event)) { return; }

    // h: help
    if (event.keyCode === 72) {
      Events.emit('openhelpmodal');
    }

    // esc: close inspector
    if (event.keyCode === 27) {
      AFRAME.INSPECTOR.close();
      return;
    }

    // w: translate
    if (event.keyCode === 87) {
      Events.emit('transformmodechanged', 'translate');
    }

    // e: rotate
    if (event.keyCode === 69) {
      Events.emit('transformmodechanged', 'rotate');
    }

    // r: scale
    if (event.keyCode === 82) {
      Events.emit('transformmodechanged', 'scale');
    }

    // g: toggle grid
    if (event.keyCode === 71) {
      Events.emit('togglegrid');
    }

    // n: new entity
    if (event.keyCode === 78) {
      Events.emit('createnewentity', {element: 'a-entity', components: {}});
    }

    // backspace & supr: remove selected entity
    if (event.keyCode === 8 || event.keyCode === 46) {
      removeSelectedEntity();
    }

    // d: clone selected entity
    if (event.keyCode === 68) {
      cloneSelectedEntity();
    }
  },
  enable: function () {
    window.addEventListener('keyup', this.onKeyUp, false);
  },
  disable: function () {
    window.removeEventListener('keyup', this.onKeyUp);
  }
};
<|endoftext|>


<commit_before>(function($){
	// 创建html标签方法
	function newTag(config) {
		var opt = $.extend({
			tag: 'span',
			css: {},
			class: '',
			html: '',
			data: {}
		}, config);
		var tag = opt.tag;
		var className = opt.class;
		var css = opt.css;
		var content = opt.html;
		var data = opt.data;
		delete(opt.tag);
		delete(opt.class);
		delete(opt.css);
		delete(opt.html);
		delete(opt.data);
		return $('<'+tag+'>').css(css).addClass(className).data(data).prop(opt).html(content);
	};
	// 扫雷
	$.fn.sweeper = function(config){
		var opt = $.extend({
			// 状态栏配置
			statusView: {
				tag: 'p',
				css: {
					height: '30px',
					padding: '10px',
					background: '#333',
					color: '#fff',
					display: 'block',
					'text-align': 'center',
					'line-height': '30px'
				},
				class: {
					statusClass: ''
				}
			},

			// 控制区配置
			ctrl: {
				tag: 'form',
				css: {
					'text-align': 'center'
				}
			},
			selectBtn: {
				tag: 'select',
				css: {
					padding: '5px 10px',
					'font-size': '16px',
					background: '#fff',
					cursor: 'pointer',
					border: '1px solid #999',
					'margin-right': '5px',
					'vertical-align': 'middle'
				},
				id: 'sweeper-select-id',
				class: '',
				name: 'sweeper-level'
			},
			startBtn: {
				type: 'submit',
				tag: 'button',
				css: {
					padding: '5px 10px',
					'font-size': '16px',
					background: '#fff',
					cursor: 'pointer',
					border: '1px solid #999',
					'margin-right': '5px',
					'vertical-align': 'middle',
				},
				id: 'sweeper-start-id',
				html: '游戏开始'
			},
			resetBtn: {
				type: 'reset',
				tag: 'button',
				css: {
					padding: '5px 10px',
					'font-size': '16px',
					background: '#fff',
					cursor: 'pointer',
					border: '1px solid #999',
					'margin-right': '5px',
					'vertical-align': 'middle',
				},
				id: 'sweeper-reset-id',
				html: '游戏重置'
			},

			// 雷区容器
			minesArea: {
				tag: 'div',
				id: 'sweeper-mines-id',
				class: '',
				css: {
					width: '100%',
					display: 'block',
					padding: 0,
					margin: 0,
					'border-top': '1px solid #333'
				}
			},
			minesTable: {
				tag: 'div',
				css: {
					display: 'table',
					margin: '0 auto'
				},
				id: 'sweeper-mines-table'
			},
			minesRow: {
				tag: 'div',
				css: {
					display: 'table-row'
				},
				class: 'sweeper-mines-row'
			},

			// 地雷样式
			mine: {
				tag: 'span',
				css: {
					display: 'table-cell',
					width: '30px',
					height: '30px',
					'line-height': '30px',
					border: '1px solid #fff',
					background: '#707070',
					margin: '-1px 0 0 -1px',
					padding: 0,
					cursor: 'pointer',
					'text-align': 'center'
				},
				class: 'sweeper-mine'
			},
			mineClearCss: {
				background: '#fff',
				border: '1px solid #999'
			},
			minIdPrefix: 'sweeper-mine-',

			// gameOver 样式
			gameOverTip: {
				css: {
					display: 'inline-bloc',
					padding: '10px',
					position: 'absolute',
					left: '40%',
					top: '50%',
					background: '#000',
					color: 'red',
					'font-size': '30px',
					cursor: 'pointer'
				},
				tag: 'span',
				html: 'GAME OVER!'

			},
			gameWinTip: {
				css: {
					padding: '10px',
					position: 'absolute',
					left: '40%',
					top: '50%',
					background: 'red',
					color: '#fff',
					'font-size': '30px',
					cursor: 'pointer',
					display: 'inline-bloc'

				},
				tag: 'span',
				html: 'WIN!!!'
			},
			ribbon: {
				tag: 'span',
				css: {
					width: 0,
					'height': '16px',
					'border-left': '8px solid blue',
					'border-right':  '8px solid blue',
					'border-bottom': '5px solid transparent'
				}
			},
			bomb: {
				tag: 'span',
				css: {
					height: 0,
					width: 0,
					'border-width': '0 0 16px 16px',
					'border-style': 'dotted',
					'border-color': 'red',
				}
			},
			bombCss: {
				background: 'red'
			}
		}, config);

		var container = $(this);
		// 视图部件
		var ctrl; // 控制面板
		var startBtn; // 开始按钮
		var resetBtn; // 重置按钮
		var selectBtn; // 游戏等级选择框
		var statusView; // 游戏状态区
		var minesArea; // 雷区外部容器
		var mines; // 雷区

		// 模型参数
		/* @property x int 地雷区x轴 */
		/* @property y int 地雷区y轴 */
		/* @property total int 地雷个数 */
		var x,y,total;
		/* @property map array 地图模型 */
		var map;
		/* @property step int */
		var step;
		/* @property level int 游戏等级 1-3*/
		var level;
		/* @property status string 游戏状态 */
		var status = 'init';
		/* @property statusArr array 游戏状态数组,游戏状态对应提示语句 */
		var statusArr = {
			'init': '等待游戏开始',
			'start': '游戏开始',
			'running': '游戏进行中',
			'over': '游戏结束',
			'win': '你赢了!',
			'reset': '游戏重置'
		}; // 游戏状态

		var view = [];

		var game = {
			run: function(){
				// 初始化视图

				// 1. 构建控制面板
				ctrl = newTag(opt.ctrl);
				// 2. 新建等级选择按钮
				selectBtn = newTag(opt.selectBtn);
				for(var i = 1; i <=3 ; i++) {
					$('<option>').val(i).html('等级'+i).appendTo(selectBtn);
				}
				// 3. 新建游戏开始按钮
				startBtn = newTag(opt.startBtn);
				// 4. 新建游戏重置按钮
				resetBtn = newTag(opt.resetBtn);

				// 5. 构建控制面板
				ctrl.append(selectBtn, startBtn, resetBtn);
				ctrl.appendTo(container);

				// 6. 构建游戏状态显示区域
				statusView = newTag(opt.statusView);
				game.setStatus('init');
				statusView.appendTo(container);

				// 7. 构建地雷区
				minesArea = newTag(opt.minesArea);
				minesArea.appendTo(container);

				// 游戏开始
				ctrl.submit(game.start);
				resetBtn.click(game.reset);

			},
			win: function(){
				game.setStatus('win');
				$('.' + opt.mine.class).unbind('mousedown');
				newTag(opt.gameWinTip).appendTo(mines);
				return false;
			},
			setStatus: function(s){
				status = s;
				statusView.html(statusArr[status]);
			},
			setLevel: function(){
				switch (level) {
					case 2:
						y = 16; x = 16; total=40;
						break;
					case 3:
						y = 16; x = 30; total= 99;
						break;
					default :
						y = 9; x = 9; total= 10;
				}
			},
			over: function(pos){
				game.setStatus('over');
				$('.' + opt.mine.class).unbind('mousedown');
				for(k in map) {
					if (view[k].data('rightClick') != 1) {
						if (map[k] < 10) {
							view[k].css(opt.mineClearCss);
							if (map[k] === 9) {
								view[k].html('*');
							} else if(map[k] != 0) {
								view[k].html(map[k]);
							}
						}
					}
				}
				newTag(opt.gameOverTip).appendTo(mines);
			},
			start: function(e){
				e.preventDefault();
				// 1. 设置游戏状态
				game.setStatus('start');
				// 2. 初始化参数
				game.init();
				// 3. 设置游戏等级
				level = parseInt(selectBtn.val());
				game.setLevel();
				// 4. 禁用游戏等级选择
				selectBtn.prop('disabled', true);
				startBtn.prop('disabled', true);

				// 5. 设置雷区
				mines = newTag(opt.minesTable);

				for (var i = 1; i <= y; i++) {
					var row = newTag(opt.minesRow);
					for (var j = 1; j <= x; j++) {
						var n = i*100 + j;
						map[n] = 0;
						// 构建地雷块
						var mineId = opt.minIdPrefix + n;
						var config = $.extend(opt.mine, {id: mineId, data: {id: n}});
						var mine = newTag(config);
						// 绑定鼠标点击事件
						mine.on('mousedown', game.minesClick);
						view[n] = mine;
						mine.appendTo(row);
					}
					row.appendTo(mines);
				}
				mines.appendTo(minesArea);
			},
			reset: function(e){
				e.preventDefault();

				game.setStatus('reset');
				selectBtn.prop('disabled', false);
				startBtn.prop('disabled',false);
				minesArea.html('');
			},
			minesClick: function(e){ // 扫雷
				e.preventDefault();
				// 禁用浏览器菜单
				$(this).contextmenu(function(){
					return false;
				});
				var ele = $(this);
				var pos = ele.data('id');
				// 单击左键
				if (e.button == 0 && ele.data('rightClick') != 1) {
					if (step == 0) {
						game.layMines(pos); // 布雷
					}
					game.sweeping(pos);// 扫雷
				}
				// 右键添加标记
				else if (e.button == 2 && ele.data('rightClick') != 1) {
					ele.data('rightClick', 1);
					var ribbon = newTag(opt.ribbon);
					ele.html(ribbon);
				}
				// 右键消除标记
				else if (e.button == 2 && ele.data('rightClick') == 1) {
					ele.data('rightClick', 0);
					ele.html('');
				}
			},
			init: function(){
				map = [];
				step = 0;
				x = 0;
				y = 0;
				total = 0;
			},
			layMines: function(pos){

				var area = game.getArea(pos, function(tempPos, arr){
					if (typeof(map[tempPos]) != 'undefined') {
						arr.push(tempPos);
					}
				});
				var t = total;
				console.log(area);
				while(t) {
					var tx = Math.ceil(Math.random()*x);
					var ty = Math.ceil(Math.random()*y);
					var tPos = ty*100+tx;
					if (map[tPos] != 9 && $.inArray(tPos, area) < 0) {
						map[tPos] = 9;

						game.getArea(tPos, function(tempPos, arr){
							if (typeof(map[tempPos]) != 'undefined' && map[tempPos] != 9) {
								map[tempPos]++;
							}
						});
						t--;
					}
				}
			},
			sweeping: function(pos){
				if (map[pos] == 9) {
					game.over();
					view[pos].css(opt.bombCss);
				}
				var queue = [];

				queue.push(pos);
				while (queue.length) {
					var p =  queue.pop();
					ele = view[p];
					if (ele.data('rightClick') != 1) {
						if (map[p] === 0) {
							map[p] += 10;
							view[p].unbind('mousedown');
							ele.css(opt.mineClearCss).html('');
							game.getArea(p, function(tempPos, arr){
								if (typeof(map[tempPos]) != 'undefined' && map[tempPos] <= 6) {
									queue.push(tempPos);
								}
							});
							step++;
						} else if (map[p] <= 6) {
							ele.css(opt.mineClearCss).html(map[p]);
							map[p] += 10;
							view[p].unbind('mousedown');
							step++;
						}
					}
				}
				if (step >= (x*y-total)) {
					game.win();
				}
				console.log('step', step);
			},
			getArea: function(pos, callback){
				var px = pos%100;
				var py = Math.floor(pos/100);
				var res = new Array();
				for (var m = py-1; m <= py + 1; m++){
					for (var n = px-1; n <= px + 1; n++) {
						var tempPos = m*100+n;

						callback(tempPos, res);
					}
				}
				return res;
			}
		};
		game.run();
	}
})(jQuery);
<commit_msg>Update minesSweeper.version2.js<commit_after>(function($){
	// 创建html标签方法
	function newTag(config) {
		var opt = $.extend({
			tag: 'span',
			css: {},
			class: '',
			html: '',
			data: {}
		}, config);
		var tag = opt.tag;
		var className = opt.class;
		var css = opt.css;
		var content = opt.html;
		var data = opt.data;
		delete(opt.tag);
		delete(opt.class);
		delete(opt.css);
		delete(opt.html);
		delete(opt.data);
		return $('<'+tag+'>').css(css).addClass(className).data(data).prop(opt).html(content);
	};
	// 扫雷
	$.fn.sweeper = function(config){
		var opt = $.extend({
			// 状态栏配置
			statusView: {
				tag: 'p',
				css: {
					height: '30px',
					padding: '10px',
					background: '#333',
					color: '#fff',
					display: 'block',
					'text-align': 'center',
					'line-height': '30px'
				},
				class: {
					statusClass: ''
				}
			},

			// 控制区配置
			ctrl: {
				tag: 'form',
				css: {
					'text-align': 'center'
				}
			},
			selectBtn: {
				tag: 'select',
				css: {
					padding: '5px 10px',
					'font-size': '16px',
					background: '#fff',
					cursor: 'pointer',
					border: '1px solid #999',
					'margin-right': '5px',
					'vertical-align': 'middle'
				},
				id: 'sweeper-select-id',
				class: '',
				name: 'sweeper-level'
			},
			startBtn: {
				type: 'submit',
				tag: 'button',
				css: {
					padding: '5px 10px',
					'font-size': '16px',
					background: '#fff',
					cursor: 'pointer',
					border: '1px solid #999',
					'margin-right': '5px',
					'vertical-align': 'middle',
				},
				id: 'sweeper-start-id',
				html: '游戏开始'
			},
			resetBtn: {
				type: 'reset',
				tag: 'button',
				css: {
					padding: '5px 10px',
					'font-size': '16px',
					background: '#fff',
					cursor: 'pointer',
					border: '1px solid #999',
					'margin-right': '5px',
					'vertical-align': 'middle',
				},
				id: 'sweeper-reset-id',
				html: '游戏重置'
			},

			// 雷区容器
			minesArea: {
				tag: 'div',
				id: 'sweeper-mines-id',
				class: '',
				css: {
					width: '100%',
					display: 'block',
					padding: 0,
					margin: 0,
					'border-top': '1px solid #333'
				}
			},
			minesTable: {
				tag: 'div',
				css: {
					display: 'table',
					margin: '0 auto'
				},
				id: 'sweeper-mines-table'
			},
			minesRow: {
				tag: 'div',
				css: {
					display: 'table-row'
				},
				class: 'sweeper-mines-row'
			},

			// 地雷样式
			mine: {
				tag: 'div',
				css: {
					display: 'block',
					width: '30px',
					height: '30px',
					'line-height': '30px',
					border: '1px solid #fff',
					background: '#707070',
					margin: '-1px 0 0 -1px',
					padding: 0,
					cursor: 'pointer',
					'text-align': 'center',
                                        float: 'left',
				},
				class: 'sweeper-mine'
			},
			mineClearCss: {
				background: '#fff',
				border: '1px solid #999'
			},
			minIdPrefix: 'sweeper-mine-',

			// gameOver 样式
			gameOverTip: {
				css: {
					display: 'inline-bloc',
					padding: '10px',
					position: 'absolute',
					left: '40%',
					top: '50%',
					background: '#000',
					color: 'red',
					'font-size': '30px',
					cursor: 'pointer'
				},
				tag: 'span',
				html: 'GAME OVER!'

			},
			gameWinTip: {
				css: {
					padding: '10px',
					position: 'absolute',
					left: '40%',
					top: '50%',
					background: 'red',
					color: '#fff',
					'font-size': '30px',
					cursor: 'pointer',
					display: 'inline-bloc'

				},
				tag: 'span',
				html: 'WIN!!!'
			},
			ribbon: {
				tag: 'span',
				css: {
					width: 0,
					'height': '16px',
					'border-left': '8px solid blue',
					'border-right':  '8px solid blue',
					'border-bottom': '5px solid transparent'
				}
			},
			bomb: {
				tag: 'span',
				css: {
					height: 0,
					width: 0,
					'border-width': '0 0 16px 16px',
					'border-style': 'dotted',
					'border-color': 'red',
				}
			},
			bombCss: {
				background: 'red'
			}
		}, config);

		var container = $(this);
		// 视图部件
		var ctrl; // 控制面板
		var startBtn; // 开始按钮
		var resetBtn; // 重置按钮
		var selectBtn; // 游戏等级选择框
		var statusView; // 游戏状态区
		var minesArea; // 雷区外部容器
		var mines; // 雷区

		// 模型参数
		/* @property x int 地雷区x轴 */
		/* @property y int 地雷区y轴 */
		/* @property total int 地雷个数 */
		var x,y,total;
		/* @property map array 地图模型 */
		var map;
		/* @property step int */
		var step;
		/* @property level int 游戏等级 1-3*/
		var level;
		/* @property status string 游戏状态 */
		var status = 'init';
		/* @property statusArr array 游戏状态数组,游戏状态对应提示语句 */
		var statusArr = {
			'init': '等待游戏开始',
			'start': '游戏开始',
			'running': '游戏进行中',
			'over': '游戏结束',
			'win': '你赢了!',
			'reset': '游戏重置'
		}; // 游戏状态

		var view = [];

		var game = {
			run: function(){
				// 初始化视图

				// 1. 构建控制面板
				ctrl = newTag(opt.ctrl);
				// 2. 新建等级选择按钮
				selectBtn = newTag(opt.selectBtn);
				for(var i = 1; i <=3 ; i++) {
					$('<option>').val(i).html('等级'+i).appendTo(selectBtn);
				}
				// 3. 新建游戏开始按钮
				startBtn = newTag(opt.startBtn);
				// 4. 新建游戏重置按钮
				resetBtn = newTag(opt.resetBtn);

				// 5. 构建控制面板
				ctrl.append(selectBtn, startBtn, resetBtn);
				ctrl.appendTo(container);

				// 6. 构建游戏状态显示区域
				statusView = newTag(opt.statusView);
				game.setStatus('init');
				statusView.appendTo(container);

				// 7. 构建地雷区
				minesArea = newTag(opt.minesArea);
				minesArea.appendTo(container);

				// 游戏开始
				ctrl.submit(game.start);
				resetBtn.click(game.reset);

			},
			win: function(){
				game.setStatus('win');
				$('.' + opt.mine.class).unbind('mousedown');
				newTag(opt.gameWinTip).appendTo(mines);
				return false;
			},
			setStatus: function(s){
				status = s;
				statusView.html(statusArr[status]);
			},
			setLevel: function(){
				switch (level) {
					case 2:
						y = 16; x = 16; total=40;
						break;
					case 3:
						y = 16; x = 30; total= 99;
						break;
					default :
						y = 9; x = 9; total= 10;
				}
			},
			over: function(pos){
				game.setStatus('over');
				$('.' + opt.mine.class).unbind('mousedown');
				for(k in map) {
					if (view[k].data('rightClick') != 1) {
						if (map[k] < 10) {
							view[k].css(opt.mineClearCss);
							if (map[k] === 9) {
								view[k].html('*');
							} else if(map[k] != 0) {
								view[k].html(map[k]);
							}
						}
					}
				}
				newTag(opt.gameOverTip).appendTo(mines);
			},
			start: function(e){
				e.preventDefault();
				// 1. 设置游戏状态
				game.setStatus('start');
				// 2. 初始化参数
				game.init();
				// 3. 设置游戏等级
				level = parseInt(selectBtn.val());
				game.setLevel();
				// 4. 禁用游戏等级选择
				selectBtn.prop('disabled', true);
				startBtn.prop('disabled', true);

				// 5. 设置雷区
				mines = newTag(opt.minesTable);

				for (var i = 1; i <= y; i++) {
					var row = newTag(opt.minesRow);
					for (var j = 1; j <= x; j++) {
						var n = i*100 + j;
						map[n] = 0;
						// 构建地雷块
						var mineId = opt.minIdPrefix + n;
						var config = $.extend(opt.mine, {id: mineId, data: {id: n}});
						var mine = newTag(config);
						// 绑定鼠标点击事件
						mine.on('mousedown', game.minesClick);
						view[n] = mine;
						mine.appendTo(row);
					}
					row.appendTo(mines);
				}
				mines.appendTo(minesArea);
			},
			reset: function(e){
				e.preventDefault();

				game.setStatus('reset');
				selectBtn.prop('disabled', false);
				startBtn.prop('disabled',false);
				minesArea.html('');
			},
			minesClick: function(e){ // 扫雷
				e.preventDefault();
				// 禁用浏览器菜单
				$(this).contextmenu(function(){
					return false;
				});
				var ele = $(this);
				var pos = ele.data('id');
				// 单击左键
				if (e.button == 0 && ele.data('rightClick') != 1) {
					if (step == 0) {
						game.layMines(pos); // 布雷
					}
					game.sweeping(pos);// 扫雷
				}
				// 右键添加标记
				else if (e.button == 2 && ele.data('rightClick') != 1) {
					ele.data('rightClick', 1);
					var ribbon = newTag(opt.ribbon);
					ele.html(ribbon);
				}
				// 右键消除标记
				else if (e.button == 2 && ele.data('rightClick') == 1) {
					ele.data('rightClick', 0);
					ele.html('');
				}
			},
			init: function(){
				map = [];
				step = 0;
				x = 0;
				y = 0;
				total = 0;
			},
			layMines: function(pos){

				var area = game.getArea(pos, function(tempPos, arr){
					if (typeof(map[tempPos]) != 'undefined') {
						arr.push(tempPos);
					}
				});
				var t = total;
				console.log(area);
				while(t) {
					var tx = Math.ceil(Math.random()*x);
					var ty = Math.ceil(Math.random()*y);
					var tPos = ty*100+tx;
					if (map[tPos] != 9 && $.inArray(tPos, area) < 0) {
						map[tPos] = 9;

						game.getArea(tPos, function(tempPos, arr){
							if (typeof(map[tempPos]) != 'undefined' && map[tempPos] != 9) {
								map[tempPos]++;
							}
						});
						t--;
					}
				}
			},
			sweeping: function(pos){
				if (map[pos] == 9) {
					game.over();
					view[pos].css(opt.bombCss);
				}
				var queue = [];

				queue.push(pos);
				while (queue.length) {
					var p =  queue.pop();
					ele = view[p];
					if (ele.data('rightClick') != 1) {
						if (map[p] === 0) {
							map[p] += 10;
							view[p].unbind('mousedown');
							ele.css(opt.mineClearCss).html('');
							game.getArea(p, function(tempPos, arr){
								if (typeof(map[tempPos]) != 'undefined' && map[tempPos] <= 6) {
									queue.push(tempPos);
								}
							});
							step++;
						} else if (map[p] <= 6) {
							ele.css(opt.mineClearCss).html(map[p]);
							map[p] += 10;
							view[p].unbind('mousedown');
							step++;
						}
					}
				}
				if (step >= (x*y-total)) {
					game.win();
				}
				console.log('step', step);
			},
			getArea: function(pos, callback){
				var px = pos%100;
				var py = Math.floor(pos/100);
				var res = new Array();
				for (var m = py-1; m <= py + 1; m++){
					for (var n = px-1; n <= px + 1; n++) {
						var tempPos = m*100+n;

						callback(tempPos, res);
					}
				}
				return res;
			}
		};
		game.run();
	}
})(jQuery);
<|endoftext|>


<commit_before>var expect = require('chai').expect;

describe("Module", function () {

    var JsCop = require("..");

    it("export classes", function () {
        expect(JsCop).to.exist;
    });

    describe("Module", function () {

        it("codeFromFunction", function () {

            var code = JsCop.Helper.codeFromFunction(function () {});
            expect(code).to.eql("(function () {})");

        });

    });

});<commit_msg>added semicolon to functionToCode function<commit_after>var expect = require('chai').expect;

describe("Module", function () {

    var JsCop = require("..");

    it("export classes", function () {
        expect(JsCop).to.exist;
    });

    describe("Module", function () {

        it("codeFromFunction", function () {

            var code = JsCop.Helper.codeFromFunction(function () {});
            expect(code).to.eql("(function () {});");

        });

    });

});<|endoftext|>


<commit_before>import { equal, raw } from 'ember-awesome-macros';
import { module, test } from 'qunit';
import compute from 'ember-macro-test-helpers/compute';

module('Integration | Macro | equal');

test('not equal returns false', function(assert) {
  compute({
    assert,
    computed: equal('source1', 'source2'),
    properties: {
      source1: 1,
      source2: 2
    },
    strictEqual: false
  });
});

test('equal but different type returns false', function(assert) {
  compute({
    assert,
    computed: equal('source1', 'source2'),
    properties: {
      source1: '2',
      source2: 2
    },
    strictEqual: false
  });
});

test('equal and same type returns true', function(assert) {
  compute({
    assert,
    computed: equal('source1', 'source2'),
    properties: {
      source1: 2,
      source2: 2
    },
    strictEqual: true
  });
});

test('checks equality for N items', function(assert) {
  let { subject } = compute({
    assert,
    computed: equal('source1', 'source2', 'source3'),
    properties: {
      source1: 2,
      source2: 2,
      source3: 1
    },
    strictEqual: false
  });

  subject.set('source3', 2);

  assert.strictEqual(subject.get('computed'), true);
});

test('it handles numbers', function(assert) {
  compute({
    assert,
    computed: equal(2, 2),
    strictEqual: true
  });
});

test('it handles nesting', function(assert) {
  compute({
    assert,
    computed: equal(raw(2), raw(2)),
    strictEqual: true
  });
});
<commit_msg>remove redundant alias test for `equal`<commit_after><|endoftext|>


<commit_before>// Generated on 2014-11-18 using generator-angular 0.10.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

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

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

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

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        protocol: 'https"',
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      proxies: [
        {
          context: '/api', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998, // the port that the data service is running on
          https: false
        },
        {
          context: '/auth', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998, // the port that the data service is running on
          https: false
        },
        {
          context: '/signin/authenticate', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9999, // the port that the data service is running on
          https: true
        },
        {
          context: '/livefeed', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998, // the port that the data service is running on
          ws: true
        }
      ],
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app),
              require('grunt-connect-proxy/lib/utils').proxyRequest
            ];

          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

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

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

    // Add vendor prefixed styles
    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
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath: /\.\.\//
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: /(\.\.\/){1,2}bower_components\//
      }
    },

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

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          //  TODO revisit this - perhaps move the hangman ones
          //'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // 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: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>', '<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // 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: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html', 'signin/{,*/}*.html', 'facebook/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: ['*.js', '!oldieshim.js'],
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'signin/{,*/}*.html',
            'facebook/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/{,*/}*.*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: '.',
          src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    }
  });

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

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

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

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

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<commit_msg>starting security tests<commit_after>// Generated on 2014-11-18 using generator-angular 0.10.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

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

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

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

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.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
      },
      proxies: [
        {
          context: '/api', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998 // the port that the data service is running on
        },
        {
          context: '/auth', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998 // the port that the data service is running on
        },
        {
          context: '/signin/authenticate', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998 // the port that the data service is running on
        },
        {
          context: '/livefeed', // the context of the data service
          host: 'localhost', // wherever the data service is running
          port: 9998, // the port that the data service is running on
          ws: true
        }
      ],
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app),
              require('grunt-connect-proxy/lib/utils').proxyRequest
            ];

          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

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

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

    // Add vendor prefixed styles
    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
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath: /\.\.\//
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: /(\.\.\/){1,2}bower_components\//
      }
    },

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

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          //  TODO revisit this - perhaps move the hangman ones
          //'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // 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: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>', '<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // 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: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html', 'signin/{,*/}*.html', 'facebook/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: ['*.js', '!oldieshim.js'],
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'signin/{,*/}*.html',
            'facebook/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/{,*/}*.*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: '.',
          src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    }
  });

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

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

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

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

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
  ]);

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


<commit_before>define(function() {

  // http://seancode.com/webventure/formats.html#GNRL

  'use strict';

  function open(item) {
    return item.getBytes().then(function(bytes) {
      switch(item.resourceID) {
        case 0x80:
          item.setDataObject(new GeneralSettingsView(bytes.buffer, bytes.byteOffset, bytes.byteLength));
          break;
        default:
          console.warn('unsupported GNRL resource ID: ' + item.resourceID);
          break;
      }
    });
  }

  function GeneralSettingsView(buffer, byteOffset, byteLength) {
    this.dataView = new DataView(buffer, byteOffset, byteLength);
  }
  GeneralSettingsView.prototype = {
    toJSON: function() {
      return {
        objectCount: this.objectCount,
        globalCount: this.globalCount,
        groupCount: this.groupCount,
        commands: this.commands,
        attributes: this.attributes,
        inventory: {
          top: this.inventoryTop,
          left: this.inventoryLeft,
          height: this.inventoryHeight,
          width: this.inventoryWidth,
          offsetX: this.inventoryOffsetX,
          offsetY: this.inventoryOffsetY,
        },
        defaultFont: this.defaultFont,
        defaultFontSize: this.defaultFontSize,
      };
    },
    get objectCount() {
      return this.dataView.getUint16(0, false);
    },
    get globalCount() {
      return this.dataView.getUint16(2, false);
    },
    get commandCount() {
      return this.dataView.getUint16(4, false);
    },
    get attributeCount() {
      return this.dataView.getUint16(6, false);
    },
    get groupCount() {
      return this.dataView.getUint16(8, false);
    },
    // unknown: 2 bytes
    get inventoryTop() {
      return this.dataView.getUint16(12, false);
    },
    get inventoryLeft() {
      return this.dataView.getUint16(14, false);
    },
    get inventoryHeight() {
      return this.dataView.getUint16(16, false);
    },
    get inventoryWidth() {
      return this.dataView.getUint16(18, false);
    },
    get inventoryOffsetY() {
      return this.dataView.getUint16(20, false);
    },
    get inventoryOffsetX() {
      return this.dataView.getUint16(22, false);
    },
    get defaultFont() {
      return this.dataView.getUint16(24, false);
    },
    get defaultFontSize() {
      return this.dataView.getUint16(26, false);
    },
    get attributeIndices() {
      var indices = new Array(this.attributeCount);
      var pos = 28;
      for (var i = 0; i < indices.length; i++) {
        indices[i] = this.dataView.getUint8(pos++);
      }
      indices.afterPos = pos;
      Object.defineProperty(this, 'attributeIndices', {value:indices});
      return indices;
    },
    get attributeMasks() {
      var masks = new Array(this.attributeCount);
      var pos = this.attributeIndices.afterPos;
      for (var i = 0; i < masks.length; i++) {
        masks[i] = this.dataView.getUint16(pos);
        pos += 2;
      }
      masks.afterPos = pos;
      Object.defineProperty(this, 'attributeMasks', {value:masks});
      return masks;
    },
    get attributeBitShifts() {
      var shifts = new Array(this.attributeCount);
      var pos = this.attributeMasks.afterPos;
      for (var i = 0; i < shifts.length; i++) {
        shifts[i] = this.dataView.getUint8(pos++);
      }
      shifts.afterPos = pos;
      Object.defineProperty(this, 'attributeBitShifts', {value:shifts});
      return shifts;
    },
    get commandArgumentCounts() {
      var counts = new Array(this.commandCount);
      var pos = this.attributeBitShifts.afterPos;
      for (var i = 0; i < counts.length; i++) {
        counts[i] = this.dataView.getUint8(pos++);
      }
      counts.afterPos = pos;
      Object.defineProperty(this, 'commandArgumentCounts', {value:counts});
      return counts;
    },
    get commandButtons() {
      var buttons = new Array(this.commandCount);
      var pos = this.commandArgumentCounts.afterPos;
      for (var i = 0; i < buttons.length; i++) {
        buttons[i] = this.dataView.getUint8(pos++);
      }
      buttons.afterPos = pos;
      Object.defineProperty(this, 'commandButtons', {value:buttons});
      return buttons;
    },
    get attributes() {
      var attributes = new Array(this.argumentCount);
      for (var i = 0; i < attributes.length; i++) {
        attributes[i] = {
          index: this.attributeIndices[i],
          mask: this.attributeMasks[i],
          bitShift: this.attributeBitShifts[i],
        };
      }
      Object.defineProperty(this, 'attributes', {value:attributes});
      return attributes;
    },
    get commands() {
      var commands = new Array(this.commandCount);
      for (var i = 0; i < commands.length; i++) {
        commands[i] = {
          argumentCount: this.commandArgumentCounts[i],
          button: this.commandButtons[i],
        };
      }
      Object.defineProperty(this, 'commands', {value:commands});
      return commands;
    },
  };

  return open;

});
<commit_msg>Add diploma geometry<commit_after>define(function() {

  // http://seancode.com/webventure/formats.html#GNRL

  'use strict';

  function open(item) {
    return item.getBytes().then(function(bytes) {
      switch(item.resourceID) {
        case 0x80:
          item.setDataObject(new GeneralSettingsView(bytes.buffer, bytes.byteOffset, bytes.byteLength));
          break;
        case 0x81:
          item.setDataObject(new DiplomaGeometryView(bytes.buffer, bytes.byteOffset, bytes.byteLength));
          break;
        default:
          console.warn('unsupported GNRL resource ID: ' + item.resourceID);
          break;
      }
    });
  }

  function GeneralSettingsView(buffer, byteOffset, byteLength) {
    this.dataView = new DataView(buffer, byteOffset, byteLength);
  }
  GeneralSettingsView.prototype = {
    toJSON: function() {
      return {
        objectCount: this.objectCount,
        globalCount: this.globalCount,
        groupCount: this.groupCount,
        commands: this.commands,
        attributes: this.attributes,
        inventory: {
          top: this.inventoryTop,
          left: this.inventoryLeft,
          height: this.inventoryHeight,
          width: this.inventoryWidth,
          offsetX: this.inventoryOffsetX,
          offsetY: this.inventoryOffsetY,
        },
        defaultFont: this.defaultFont,
        defaultFontSize: this.defaultFontSize,
      };
    },
    get objectCount() {
      return this.dataView.getUint16(0, false);
    },
    get globalCount() {
      return this.dataView.getUint16(2, false);
    },
    get commandCount() {
      return this.dataView.getUint16(4, false);
    },
    get attributeCount() {
      return this.dataView.getUint16(6, false);
    },
    get groupCount() {
      return this.dataView.getUint16(8, false);
    },
    // unknown: 2 bytes
    get inventoryTop() {
      return this.dataView.getUint16(12, false);
    },
    get inventoryLeft() {
      return this.dataView.getUint16(14, false);
    },
    get inventoryHeight() {
      return this.dataView.getUint16(16, false);
    },
    get inventoryWidth() {
      return this.dataView.getUint16(18, false);
    },
    get inventoryOffsetY() {
      return this.dataView.getUint16(20, false);
    },
    get inventoryOffsetX() {
      return this.dataView.getUint16(22, false);
    },
    get defaultFont() {
      return this.dataView.getUint16(24, false);
    },
    get defaultFontSize() {
      return this.dataView.getUint16(26, false);
    },
    get attributeIndices() {
      var indices = new Array(this.attributeCount);
      var pos = 28;
      for (var i = 0; i < indices.length; i++) {
        indices[i] = this.dataView.getUint8(pos++);
      }
      indices.afterPos = pos;
      Object.defineProperty(this, 'attributeIndices', {value:indices});
      return indices;
    },
    get attributeMasks() {
      var masks = new Array(this.attributeCount);
      var pos = this.attributeIndices.afterPos;
      for (var i = 0; i < masks.length; i++) {
        masks[i] = this.dataView.getUint16(pos);
        pos += 2;
      }
      masks.afterPos = pos;
      Object.defineProperty(this, 'attributeMasks', {value:masks});
      return masks;
    },
    get attributeBitShifts() {
      var shifts = new Array(this.attributeCount);
      var pos = this.attributeMasks.afterPos;
      for (var i = 0; i < shifts.length; i++) {
        shifts[i] = this.dataView.getUint8(pos++);
      }
      shifts.afterPos = pos;
      Object.defineProperty(this, 'attributeBitShifts', {value:shifts});
      return shifts;
    },
    get commandArgumentCounts() {
      var counts = new Array(this.commandCount);
      var pos = this.attributeBitShifts.afterPos;
      for (var i = 0; i < counts.length; i++) {
        counts[i] = this.dataView.getUint8(pos++);
      }
      counts.afterPos = pos;
      Object.defineProperty(this, 'commandArgumentCounts', {value:counts});
      return counts;
    },
    get commandButtons() {
      var buttons = new Array(this.commandCount);
      var pos = this.commandArgumentCounts.afterPos;
      for (var i = 0; i < buttons.length; i++) {
        buttons[i] = this.dataView.getUint8(pos++);
      }
      buttons.afterPos = pos;
      Object.defineProperty(this, 'commandButtons', {value:buttons});
      return buttons;
    },
    get attributes() {
      var attributes = new Array(this.argumentCount);
      for (var i = 0; i < attributes.length; i++) {
        attributes[i] = {
          index: this.attributeIndices[i],
          mask: this.attributeMasks[i],
          bitShift: this.attributeBitShifts[i],
        };
      }
      Object.defineProperty(this, 'attributes', {value:attributes});
      return attributes;
    },
    get commands() {
      var commands = new Array(this.commandCount);
      for (var i = 0; i < commands.length; i++) {
        commands[i] = {
          argumentCount: this.commandArgumentCounts[i],
          button: this.commandButtons[i],
        };
      }
      Object.defineProperty(this, 'commands', {value:commands});
      return commands;
    },
  };

  function DiplomaGeometryView(buffer, byteOffset, byteLength) {
    this.dataView = new DataView(buffer, byteOffset, byteLength);
  }
  DiplomaGeometryView.prototype = {
    toJSON: function() {
      return {
        signatureFont: this.signatureFont,
        signatureSize: this.signatureSize,
        top: this.top,
        left: this.left,
        bottom: this.bottom,
        right: this.right,
      };
    },
    get signatureFont() {
      return this.dataView.getUint16(0, false);
    },
    get signatureSize() {
      return this.dataView.getUint16(2, false);
    },
    get top() {
      return this.dataView.getUint16(4, false);
    },
    get left() {
      return this.dataView.getUint16(6, false);
    },
    get bottom() {
      return this.dataView.getUint16(8, false);
    },
    get right() {
      return this.dataView.getUint16(10, false);
    },
  };

  return open;

});
<|endoftext|>