<commit_before>const path = require('path')
const webpack = require('webpack')
const AssetsPlugin = require('assets-webpack-plugin')
const pkg = require('./package.json')
const dev_config = require('./dev_config.json')

const HOST = 'localhost' //dev_config.http.listen
const PORT = dev_config.http.port

const isDebug = global.DEBUG === false ? false : !process.argv.includes('--release')
const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v')
// Hot Module Replacement (HMR)
const useHMR = !!global.HMR
const babelConfig = Object.assign({}, pkg.babel, {
  babelrc: false,
  cacheDirectory: useHMR,
})

// Webpack configuration (main.js => public/dist/main.{hash}.js)
// http://webpack.github.io/docs/configuration.html
const config = {

  // The base directory for resolving the entry option
  context: __dirname,

  // The entry point for the bundle
  entry: [
    /* Material Design Lite (https://getmdl.io) */
    '!!style!css!react-mdl/extra/material.min.css',
    'react-mdl/extra/material.min.js',
    /* The main entry point of your JavaScript application */
    './main.js',
  ],

  // Options affecting the output of the compilation
  output: {
    path: path.resolve(__dirname, './public/dist'),
    publicPath: 'http://' + HOST + ':' + PORT + '/dist/',
    filename: isDebug ? '[name].js?[hash]' : '[name].[hash].js',
    chunkFilename: isDebug ? '[id].js?[chunkhash]' : '[id].[chunkhash].js',
    sourcePrefix: '  ',
  },

  // Switch loaders to debug or release mode
  debug: isDebug,

  // Developer tool to enhance debugging, source maps
  // http://webpack.github.io/docs/configuration.html#devtool
  devtool: isDebug ? 'source-map' : false,

  // What information should be printed to the console
  stats: {
    colors: true,
    reasons: isDebug,
    hash: isVerbose,
    version: isVerbose,
    timings: true,
    chunks: isVerbose,
    chunkModules: isVerbose,
    cached: isVerbose,
    cachedAssets: isVerbose,
  },

  // The list of plugins for Webpack compiler
  plugins: [
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
      __DEV__: isDebug,
    }),
    // Emit a JSON file with assets paths
    // https://github.com/sporto/assets-webpack-plugin#options
    new AssetsPlugin({
      path: path.resolve(__dirname, './public/dist'),
      filename: 'assets.json',
      prettyPrint: true,
    }),
  ],

  // Options affecting the normal modules
  module: {
    loaders: [
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        test: /\.jsx?$/,
        include: [
          path.resolve(__dirname, './actions'),
          path.resolve(__dirname, './components'),
          path.resolve(__dirname, './core'),
          path.resolve(__dirname, './pages'),
          path.resolve(__dirname, './main.js'),
          path.resolve(__dirname, './helpers'),
          path.resolve(__dirname, './redux'),
          path.resolve(__dirname, './stix-to-html'),
          path.resolve(__dirname, './Saxonce')
        ],
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      },
      {
        test: /\.json$/,
        exclude: [
          path.resolve(__dirname, './routes.json'),
          path.resolve(__dirname, './dev_config.json'),
        ],
        loader: 'json-loader',
      },
      {
        test: /\.json$/,
        include: [
          path.resolve(__dirname, './routes.json'),
        ],
        loaders: [
          `babel-loader?${JSON.stringify(babelConfig)}`,
          path.resolve(__dirname, './utils/routes-loader.js'),
        ],
      },
      {
        test: /\.md$/,
        loader: path.resolve(__dirname, './utils/markdown-loader.js'),
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
        loader: 'url-loader?limit=10000',
      },
      {
        test: /\.(eot|ttf|wav|mp3)$/,
        loader: 'file-loader',
      }
    ]
  },

  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  },

  // The list of plugins for PostCSS
  // https://github.com/postcss/postcss
  postcss(bundler) {
    return [
      // Transfer @import rule by inlining content, e.g. @import 'normalize.css'
      // https://github.com/postcss/postcss-import
      require('postcss-import')({ addDependencyTo: bundler }),
      // W3C variables, e.g. :root { --color: red } div { background: var(--color) }
      // https://github.com/postcss/postcss-custom-properties
      require('postcss-custom-properties')(),
      // W3C CSS Custom Media Queries, e.g. @custom-media --small-viewport (max-width: 30em)
      // https://github.com/postcss/postcss-custom-media
      require('postcss-custom-media')(),
      // CSS4 Media Queries, e.g. @media screen and (width >= 500px) and (width <= 1200px) { }
      // https://github.com/postcss/postcss-media-minmax
      require('postcss-media-minmax')(),
      // W3C CSS Custom Selectors, e.g. @custom-selector :--heading h1, h2, h3, h4, h5, h6
      // https://github.com/postcss/postcss-custom-selectors
      require('postcss-custom-selectors')(),
      // W3C calc() function, e.g. div { height: calc(100px - 2em) }
      // https://github.com/postcss/postcss-calc
      require('postcss-calc')(),
      // Allows you to nest one style rule inside another
      // https://github.com/jonathantneal/postcss-nesting
      require('postcss-nesting')(),
      // W3C color() function, e.g. div { background: color(red alpha(90%)) }
      // https://github.com/postcss/postcss-color-function
      require('postcss-color-function')(),
      // Convert CSS shorthand filters to SVG equivalent, e.g. .blur { filter: blur(4px) }
      // https://github.com/iamvdo/pleeease-filters
      require('pleeease-filters')(),
      // Generate pixel fallback for "rem" units, e.g. div { margin: 2.5rem 2px 3em 100% }
      // https://github.com/robwierzbowski/node-pixrem
      require('pixrem')(),
      // W3C CSS Level4 :matches() pseudo class, e.g. p:matches(:first-child, .special) { }
      // https://github.com/postcss/postcss-selector-matches
      require('postcss-selector-matches')(),
      // Transforms :not() W3C CSS Level 4 pseudo class to :not() CSS Level 3 selectors
      // https://github.com/postcss/postcss-selector-not
      require('postcss-selector-not')(),
      // Postcss flexbox bug fixer
      // https://github.com/luisrudge/postcss-flexbugs-fixes
      require('postcss-flexbugs-fixes')(),
      // Add vendor prefixes to CSS rules using values from caniuse.com
      // https://github.com/postcss/autoprefixer
      require('autoprefixer')(),
    ]
  }
}

// Optimize the bundle in release (production) mode
if (!isDebug) {
  config.plugins.push(new webpack.optimize.DedupePlugin())
  config.plugins.push(new webpack.optimize.UglifyJsPlugin({ compress: { warnings: isVerbose } }))
  config.plugins.push(new webpack.optimize.AggressiveMergingPlugin())
}

// Hot Module Replacement (HMR) + React Hot Reload
if (isDebug && useHMR) {
  babelConfig.plugins.unshift('react-hot-loader/babel')
  config.entry.unshift('react-hot-loader/patch', 'webpack-hot-middleware/client')
  config.plugins.push(new webpack.HotModuleReplacementPlugin())
  config.plugins.push(new webpack.NoErrorsPlugin())
}

module.exports = config
<commit_msg>Update webpack.config.js<commit_after>const path = require('path')
const webpack = require('webpack')
const AssetsPlugin = require('assets-webpack-plugin')
const pkg = require('./package.json')
const dev_config = require('./dev_config.json')

const HOST = dev_config.http.listen
const PORT = dev_config.http.port

const isDebug = global.DEBUG === false ? false : !process.argv.includes('--release')
const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v')
// Hot Module Replacement (HMR)
const useHMR = !!global.HMR
const babelConfig = Object.assign({}, pkg.babel, {
  babelrc: false,
  cacheDirectory: useHMR,
})

// Webpack configuration (main.js => public/dist/main.{hash}.js)
// http://webpack.github.io/docs/configuration.html
const config = {

  // The base directory for resolving the entry option
  context: __dirname,

  // The entry point for the bundle
  entry: [
    /* Material Design Lite (https://getmdl.io) */
    '!!style!css!react-mdl/extra/material.min.css',
    'react-mdl/extra/material.min.js',
    /* The main entry point of your JavaScript application */
    './main.js',
  ],

  // Options affecting the output of the compilation
  output: {
    path: path.resolve(__dirname, './public/dist'),
    publicPath: 'http://' + HOST + ':' + PORT + '/dist/',
    filename: isDebug ? '[name].js?[hash]' : '[name].[hash].js',
    chunkFilename: isDebug ? '[id].js?[chunkhash]' : '[id].[chunkhash].js',
    sourcePrefix: '  ',
  },

  // Switch loaders to debug or release mode
  debug: isDebug,

  // Developer tool to enhance debugging, source maps
  // http://webpack.github.io/docs/configuration.html#devtool
  devtool: isDebug ? 'source-map' : false,

  // What information should be printed to the console
  stats: {
    colors: true,
    reasons: isDebug,
    hash: isVerbose,
    version: isVerbose,
    timings: true,
    chunks: isVerbose,
    chunkModules: isVerbose,
    cached: isVerbose,
    cachedAssets: isVerbose,
  },

  // The list of plugins for Webpack compiler
  plugins: [
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
      __DEV__: isDebug,
    }),
    // Emit a JSON file with assets paths
    // https://github.com/sporto/assets-webpack-plugin#options
    new AssetsPlugin({
      path: path.resolve(__dirname, './public/dist'),
      filename: 'assets.json',
      prettyPrint: true,
    }),
  ],

  // Options affecting the normal modules
  module: {
    loaders: [
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        test: /\.jsx?$/,
        include: [
          path.resolve(__dirname, './actions'),
          path.resolve(__dirname, './components'),
          path.resolve(__dirname, './core'),
          path.resolve(__dirname, './pages'),
          path.resolve(__dirname, './main.js'),
          path.resolve(__dirname, './helpers'),
          path.resolve(__dirname, './redux'),
          path.resolve(__dirname, './stix-to-html'),
          path.resolve(__dirname, './Saxonce')
        ],
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      },
      {
        test: /\.json$/,
        exclude: [
          path.resolve(__dirname, './routes.json'),
          path.resolve(__dirname, './dev_config.json'),
        ],
        loader: 'json-loader',
      },
      {
        test: /\.json$/,
        include: [
          path.resolve(__dirname, './routes.json'),
        ],
        loaders: [
          `babel-loader?${JSON.stringify(babelConfig)}`,
          path.resolve(__dirname, './utils/routes-loader.js'),
        ],
      },
      {
        test: /\.md$/,
        loader: path.resolve(__dirname, './utils/markdown-loader.js'),
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
        loader: 'url-loader?limit=10000',
      },
      {
        test: /\.(eot|ttf|wav|mp3)$/,
        loader: 'file-loader',
      }
    ]
  },

  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  },

  // The list of plugins for PostCSS
  // https://github.com/postcss/postcss
  postcss(bundler) {
    return [
      // Transfer @import rule by inlining content, e.g. @import 'normalize.css'
      // https://github.com/postcss/postcss-import
      require('postcss-import')({ addDependencyTo: bundler }),
      // W3C variables, e.g. :root { --color: red } div { background: var(--color) }
      // https://github.com/postcss/postcss-custom-properties
      require('postcss-custom-properties')(),
      // W3C CSS Custom Media Queries, e.g. @custom-media --small-viewport (max-width: 30em)
      // https://github.com/postcss/postcss-custom-media
      require('postcss-custom-media')(),
      // CSS4 Media Queries, e.g. @media screen and (width >= 500px) and (width <= 1200px) { }
      // https://github.com/postcss/postcss-media-minmax
      require('postcss-media-minmax')(),
      // W3C CSS Custom Selectors, e.g. @custom-selector :--heading h1, h2, h3, h4, h5, h6
      // https://github.com/postcss/postcss-custom-selectors
      require('postcss-custom-selectors')(),
      // W3C calc() function, e.g. div { height: calc(100px - 2em) }
      // https://github.com/postcss/postcss-calc
      require('postcss-calc')(),
      // Allows you to nest one style rule inside another
      // https://github.com/jonathantneal/postcss-nesting
      require('postcss-nesting')(),
      // W3C color() function, e.g. div { background: color(red alpha(90%)) }
      // https://github.com/postcss/postcss-color-function
      require('postcss-color-function')(),
      // Convert CSS shorthand filters to SVG equivalent, e.g. .blur { filter: blur(4px) }
      // https://github.com/iamvdo/pleeease-filters
      require('pleeease-filters')(),
      // Generate pixel fallback for "rem" units, e.g. div { margin: 2.5rem 2px 3em 100% }
      // https://github.com/robwierzbowski/node-pixrem
      require('pixrem')(),
      // W3C CSS Level4 :matches() pseudo class, e.g. p:matches(:first-child, .special) { }
      // https://github.com/postcss/postcss-selector-matches
      require('postcss-selector-matches')(),
      // Transforms :not() W3C CSS Level 4 pseudo class to :not() CSS Level 3 selectors
      // https://github.com/postcss/postcss-selector-not
      require('postcss-selector-not')(),
      // Postcss flexbox bug fixer
      // https://github.com/luisrudge/postcss-flexbugs-fixes
      require('postcss-flexbugs-fixes')(),
      // Add vendor prefixes to CSS rules using values from caniuse.com
      // https://github.com/postcss/autoprefixer
      require('autoprefixer')(),
    ]
  }
}

// Optimize the bundle in release (production) mode
if (!isDebug) {
  config.plugins.push(new webpack.optimize.DedupePlugin())
  config.plugins.push(new webpack.optimize.UglifyJsPlugin({ compress: { warnings: isVerbose } }))
  config.plugins.push(new webpack.optimize.AggressiveMergingPlugin())
}

// Hot Module Replacement (HMR) + React Hot Reload
if (isDebug && useHMR) {
  babelConfig.plugins.unshift('react-hot-loader/babel')
  config.entry.unshift('react-hot-loader/patch', 'webpack-hot-middleware/client')
  config.plugins.push(new webpack.HotModuleReplacementPlugin())
  config.plugins.push(new webpack.NoErrorsPlugin())
}

module.exports = config
<|endoftext|>


<commit_before>var fs = require('fs')

var config = {};

//set defaults
config.clientId = '2574228432.104777884963';
config.port = 3004;
config.axosoftClientId = '4b62de06-1f39-41f7-ae38-37ec5262478a'

var prefix = process.env.BUILD_LIFECYCLE;

if (prefix == 'production') {
    prefix = '';
}

config.redirectUri = prefix + 'slackbot.axosoft.com:3004';
config.mongoUri = 'mongodb://' + prefix + 'slackbot.axosoft.com:27017/slackData'

if (process.env.AXO_slackbot_secret && process.env.AXO_slackbot_axosoft_secret) {
console.log('updating config file...')
    config.clientSecret = process.env.AXO_slackbot_secret;
    config.axosoftClientSecret = process.env.AXO_slackbot_axosoft_secret;
}

// write to file
// Don't want to write secrets to the log file...
//console.log(config);
fs.writeFileSync('./config.json', JSON.stringify(config));<commit_msg>update mongo config<commit_after>var fs = require('fs')

var config = {};

//set defaults
config.clientId = '2574228432.104777884963';
config.port = 3004;
config.axosoftClientId = '4b62de06-1f39-41f7-ae38-37ec5262478a'

var prefix = process.env.BUILD_LIFECYCLE;

if (prefix == 'production') {
    prefix = '';
}

config.redirectUri = prefix + 'slackbot.axosoft.com:3004';
config.mongoUri = 'mongodb://localhost:27017/slackData'

if (process.env.AXO_slackbot_secret && process.env.AXO_slackbot_axosoft_secret) {
console.log('updating config file...')
    config.clientSecret = process.env.AXO_slackbot_secret;
    config.axosoftClientSecret = process.env.AXO_slackbot_axosoft_secret;
}

// write to file
// Don't want to write secrets to the log file...
//console.log(config);
fs.writeFileSync('./config.json', JSON.stringify(config));<|endoftext|>


<commit_before>/*
Plugin Name: amCharts Auto Guides
Description: Automatically add guides to mark out preset days, like weekends
using guides.
Author: Martynas Majeris, amCharts
Version: 1.0.3
Author URI: http://www.amcharts.com/

Copyright 2016 amCharts

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Please note that the above license covers only this plugin. It by all means does
not apply to any other amCharts products that are covered by different licenses.
*/

/* globals AmCharts */
/* jshint -W061 */

/**
 * To enable add "autoGuides" block in "categoryAxis":
 *
 * "autoGuides": {
 *    "days": [ 0, 6 ],
 *    "hours": [ 19, 20, 21, 22, 23, 0, 1, 2, 3, 4 , 5, 6 ],
 *    "lineColor": "#000",
 *    "lineAlpha": 0.2,
 *    "fillColor": "#000",
 *    "fillAlpha": 0.2
 *  }
 *
 * The "autoGuides" accepts any parameter that guide has, except "date/toDate"
 * and "expand" * that will be overwritten
 * http://docs.amcharts.com/3/javascriptstockchart/Guide
 */

/**
 * Define a global function which can be used outside or inside
 */
AmCharts.autoGuidesProcess = function( chart, axis, dataProvider, categoryField ) {
  /**
   * Check if all required settings are set
   */
  if ( axis === undefined ||
    axis.autoGuides === undefined ||
    axis.parseDates !== true )
    return;

  /**
   * Resolves any variable into a Date object
   */
  function getDate( val, format ) {
    if ( typeof val === "string" && format !== undefined ) {
      // try parse date from string using dataDateFormat
      return AmCharts.stringToDate( val, format );
    } else {
      // last resort: dump everything into Date constructor
      // and let browser handle it
      return new Date( val );
    }
  }

  /**
   * Used to check if both dates are the same so
   * we don't mark the same day again with a guide
   */
  function isSameDay( lhs, rhs ) {
    if ( lhs === undefined || rhs === undefined ) {
      return false;
    } else {
      var normalizedDate = new Date( rhs );
      normalizedDate.setHours( 0, 0, 0, 0 );
      return lhs.getTime() === normalizedDate.getTime();
    }
  }

  /**
   * Init guides array
   */
  if ( axis.guides === undefined )
    axis.guides = [];

  /**
   * Set defaults
   */
  var config = axis.autoGuides;
  if ( config.days === undefined && config.hours === undefined )
    config.days = [ 0, 6 ]; // Sunday and Saturday

  if ( config.days === undefined )
    config.days = [];

  if ( config.hours === undefined )
    config.hours = [];

  /**
   * Function overlays guide properties over new object
   */
  function populateGuide( guide, config ) {
    for ( var x in config ) {
      if ( config.hasOwnProperty( x ) && x != "days" && x != "hours" ) {
        guide[ x ] = config[ x ];
      }
    }
  }

  /**
   * Iterate thorugh each day in the range of data
   */
  var firstDate = getDate( dataProvider[ 0 ][ categoryField ], chart.dataDateFormat );
  var lastDate = getDate( dataProvider[ dataProvider.length - 1 ][ categoryField ], chart.dataDateFormat );
  lastDate.setHours( 23, 59, 59, 999 );
  for ( var t = firstDate.getTime(); t <= lastDate.getTime(); t += 86400000 ) {

    // get current date
    var date = new Date( t );

    /**
     * Populate days
     */
    if ( config.days.indexOf( date.getDay() ) !== -1 ) {

      // calculate beginning and end of day
      var start = new Date( date );
      start.setHours( 0, 0, 0, 0 );
      var end = new Date( start );
      end.setHours( 23, 59, 59, 999 );

      // create guide
      var guide = {
        "date": start,
        "toDate": end,
        "expand": true
      };

      // translate all settings of the guide
      populateGuide( guide, config );

      // add guide
      axis.guides.push( guide );
    }

    /**
     * Populate hours for this day
     */
    for ( var h = 0; h < config.hours.length; h++ ) {
      var hour = config.hours[ h ];

      // calculate beginning and end of day
      var start = new Date( date );
      start.setHours( hour, 0, 0, 0 );
      var end = new Date( start );
      end.setHours( hour, 59, 59, 999 );

      // create guide
      var guide = {
        "date": start,
        "toDate": end,
        "expand": true
      };

      // translate all settings of the guide
      populateGuide( guide, config );

      // add guide
      axis.guides.push( guide );

    }
  }
};

/**
 * Handle chart load
 */
AmCharts.addInitHandler( function( chart ) {

  if ( chart.type === "stock" ) {

    /**
     * Stock chart
     */

    // get main data set
    var dataSet = chart.dataSets[ 0 ];

    // Data Loader used?
    var loader = dataSet.dataLoader;
    if ( loader !== undefined && loader.url !== undefined ) {
      if ( loader.complete ) {
        loader._complete = loader.complete;
      }
      loader.complete = function( chart ) {
        // call original complete
        if ( loader._complete )
          loader._complete.call( this, chart );

        // now let's do our thing
        for ( var i = 0; i < chart.panels.length; i++ ) {
          var panel = chart.panels[ i ];
          AmCharts.autoGuidesProcess( panel, panel.categoryAxis, dataSet.dataProvider, dataSet.categoryField );
        }
      };
    } else {

      // process each panel
      for ( var i = 0; i < chart.panels.length; i++ ) {
        var panel = chart.panels[ i ];
        AmCharts.autoGuidesProcess( panel, panel.categoryAxis, dataSet.dataProvider, dataSet.categoryField );
      }

    }

  } else {

    /**
     * Serial chart
     */

    // double check if it's not a Stock Panel
    if ( typeof chart.stockChart !== 'undefined' )
      return;

    // check for Data Loader
    var loader = chart.dataLoader;
    if ( loader !== undefined && loader.url !== undefined ) {
      if ( loader.complete ) {
        loader._complete = loader.complete;
      }
      loader.complete = function( chart ) {
        // call original complete
        if ( loader._complete )
          loader._complete.call( this, chart );

        // now let's do our thing
        AmCharts.autoGuidesProcess( chart, chart.categoryAxis, chart.dataProvider, chart.categoryField );
      };
    } else {
      AmCharts.autoGuidesProcess( chart, chart.categoryAxis, chart.dataProvider, chart.categoryField );
    }

  }

}, [ "serial", "stock" ] );<commit_msg>Update autoGuides.js<commit_after>/*
Plugin Name: amCharts Auto Guides
Description: Automatically add guides to mark out preset days, like weekends
using guides.
Author: Martynas Majeris, amCharts
Version: 1.0.3
Author URI: http://www.amcharts.com/

Copyright 2016 amCharts

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Please note that the above license covers only this plugin. It by all means does
not apply to any other amCharts products that are covered by different licenses.
*/

/* globals AmCharts */
/* jshint -W061 */

/**
 * To enable add "autoGuides" block in "categoryAxis":
 *
 * "autoGuides": {
 *    "days": [ 0, 6 ],
 *    "hours": [ 19, 20, 21, 22, 23, 0, 1, 2, 3, 4 , 5, 6 ],
 *    "lineColor": "#000",
 *    "lineAlpha": 0.2,
 *    "fillColor": "#000",
 *    "fillAlpha": 0.2
 *  }
 *
 * The "autoGuides" accepts any parameter that guide has, except "date/toDate"
 * and "expand" * that will be overwritten
 * http://docs.amcharts.com/3/javascriptstockchart/Guide
 */

/**
 * Define a global function which can be used outside or inside
 */
AmCharts.autoGuidesProcess = function( chart, axis, dataProvider, categoryField ) {
  /**
   * Check if all required settings are set
   */
  if ( axis === undefined ||
    axis.autoGuides === undefined ||
    axis.parseDates !== true )
    return;

  /**
   * Resolves any variable into a Date object
   */
  function getDate( val, format ) {
    if ( typeof val === "string" && format !== undefined ) {
      // try parse date from string using dataDateFormat
      return AmCharts.stringToDate( val, format );
    } else {
      // last resort: dump everything into Date constructor
      // and let browser handle it
      return new Date( val );
    }
  }

  /**
   * Used to check if both dates are the same so
   * we don't mark the same day again with a guide
   */
  function isSameDay( lhs, rhs ) {
    if ( lhs === undefined || rhs === undefined ) {
      return false;
    } else {
      var normalizedDate = new Date( rhs );
      normalizedDate.setHours( 0, 0, 0, 0 );
      return lhs.getTime() === normalizedDate.getTime();
    }
  }

  /**
   * Init guides array
   */
  if ( axis.guides === undefined )
    axis.guides = [];

  /**
   * Set defaults
   */
  var config = axis.autoGuides;
  if ( config.days === undefined && config.hours === undefined )
    config.days = [ 0, 6 ]; // Sunday and Saturday

  if ( config.days === undefined )
    config.days = [];

  if ( config.hours === undefined )
    config.hours = [];

  /**
   * Function overlays guide properties over new object
   */
  function populateGuide( guide, config ) {
    for ( var x in config ) {
      if ( config.hasOwnProperty( x ) && x != "days" && x != "hours" ) {
        guide[ x ] = config[ x ];
      }
    }
  }

  /**
   * Iterate thorugh each day in the range of data
   */
  var firstDate = getDate( dataProvider[ 0 ][ categoryField ], chart.dataDateFormat );
  var lastDate = getDate( dataProvider[ dataProvider.length - 1 ][ categoryField ], chart.dataDateFormat );
  lastDate.setHours( 23, 59, 59, 999 );
  for ( var t = firstDate.getTime(); t <= lastDate.getTime(); t += 86400000 ) {

    // get current date
    var date = new Date( t );

    /**
     * Populate days
     */
    if ( config.days.indexOf( date.getDay() ) !== -1 ) {

      // calculate beginning and end of day
      if(AmCharts.useUTC) {
          start = new Date( Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0));
          end = new Date( Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 23, 59, 59, 999));
      }
      else {
          start = new Date( date );
          start.setHours( 0, 0, 0, 0 );
          end = new Date( start );
          end.setHours( 23, 59, 59, 999 );
      }

      // create guide
      var guide = {
        "date": start,
        "toDate": end,
        "expand": true
      };

      // translate all settings of the guide
      populateGuide( guide, config );

      // add guide
      axis.guides.push( guide );
    }

    /**
     * Populate hours for this day
     */
    for ( var h = 0; h < config.hours.length; h++ ) {
      var hour = config.hours[ h ];

      // calculate beginning and end of day
      var start = new Date( date );
      start.setHours( hour, 0, 0, 0 );
      var end = new Date( start );
      end.setHours( hour, 59, 59, 999 );

      // create guide
      var guide = {
        "date": start,
        "toDate": end,
        "expand": true
      };

      // translate all settings of the guide
      populateGuide( guide, config );

      // add guide
      axis.guides.push( guide );

    }
  }
};

/**
 * Handle chart load
 */
AmCharts.addInitHandler( function( chart ) {

  if ( chart.type === "stock" ) {

    /**
     * Stock chart
     */

    // get main data set
    var dataSet = chart.dataSets[ 0 ];

    // Data Loader used?
    var loader = dataSet.dataLoader;
    if ( loader !== undefined && loader.url !== undefined ) {
      if ( loader.complete ) {
        loader._complete = loader.complete;
      }
      loader.complete = function( chart ) {
        // call original complete
        if ( loader._complete )
          loader._complete.call( this, chart );

        // now let's do our thing
        for ( var i = 0; i < chart.panels.length; i++ ) {
          var panel = chart.panels[ i ];
          AmCharts.autoGuidesProcess( panel, panel.categoryAxis, dataSet.dataProvider, dataSet.categoryField );
        }
      };
    } else {

      // process each panel
      for ( var i = 0; i < chart.panels.length; i++ ) {
        var panel = chart.panels[ i ];
        AmCharts.autoGuidesProcess( panel, panel.categoryAxis, dataSet.dataProvider, dataSet.categoryField );
      }

    }

  } else {

    /**
     * Serial chart
     */

    // double check if it's not a Stock Panel
    if ( typeof chart.stockChart !== 'undefined' )
      return;

    // check for Data Loader
    var loader = chart.dataLoader;
    if ( loader !== undefined && loader.url !== undefined ) {
      if ( loader.complete ) {
        loader._complete = loader.complete;
      }
      loader.complete = function( chart ) {
        // call original complete
        if ( loader._complete )
          loader._complete.call( this, chart );

        // now let's do our thing
        AmCharts.autoGuidesProcess( chart, chart.categoryAxis, chart.dataProvider, chart.categoryField );
      };
    } else {
      AmCharts.autoGuidesProcess( chart, chart.categoryAxis, chart.dataProvider, chart.categoryField );
    }

  }

}, [ "serial", "stock" ] );
<|endoftext|>


<commit_before>function plot_ring(ring, colors) {
 $.plot($("#ring"), ring, {
  series: {
   pie: {
    show: true,
    radius: 0.9,
    innerRadius: 0.4,
    label: {
     show: true,
     radius: 1.0,
     formatter: function(label, series) {
      return '
'+label+'
';
     },
     background: { opacity: 0.5, color: '#000000' }
    }
   }
  },
  legend: { show: false },
  grid: { hoverable: true, clickable: true },
  colors: colors
 });
 $("#ring").bind("plothover", pieHover);
 $("#ring").bind("plotclick", pieClick);
}

function pieHover(event, pos, obj) {
 if (!obj)
  return;
 percent = parseFloat(obj.series.percent).toFixed(2);
 $("#ringhover").html(''+obj.series.label+' ('+percent+'%)');
}

function pieClick(event, pos, obj) {
 if (!obj)
  return;
 percent = parseFloat(obj.series.percent).toFixed(2);
 alert(''+obj.series.label+': '+percent+'%');
}
<commit_msg>web debug interface: hide labels from ring charts if there percentage is lower than 3%<commit_after>function plot_ring(ring, colors) {
 $.plot($("#ring"), ring, {
  series: {
   pie: {
    show: true,
    radius: 0.9,
    innerRadius: 0.4,
    label: {
     show: true,
     radius: 1.0,
     formatter: function(label, series) {
      return '
'+label+'
';
     },
     background: { opacity: 0.5, color: '#000000' },
     threshold: 0.03
    }
   }
  },
  legend: { show: false },
  grid: { hoverable: true, clickable: true },
  colors: colors
 });
 $("#ring").bind("plothover", pieHover);
 $("#ring").bind("plotclick", pieClick);
}

function pieHover(event, pos, obj) {
 if (!obj)
  return;
 percent = parseFloat(obj.series.percent).toFixed(2);
 $("#ringhover").html(''+obj.series.label+' ('+percent+'%)');
}

function pieClick(event, pos, obj) {
 if (!obj)
  return;
 percent = parseFloat(obj.series.percent).toFixed(2);
 alert(''+obj.series.label+': '+percent+'%');
}
<|endoftext|>


<commit_before>/**
 * @jsx React.DOM
 */

var React = require('react');

var Router = require('react-router');
var Link = Router.Link;

var Tag = require('./tag');

var About = React.createClass({
	render: function(){
		return (

About

Enough Records is a netlabel (wikipedia link). We been active since 2001 and have no focus on any specific genre.

Mailing List

Subscribe to our announcemente mailing list

	<form action="//scene.us3.list-manage.com/subscribe/post?u=f8c1f1dae22f9657c634ea871&id=163fce62d4" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>

		<input type="email" name="EMAIL" class="email" id="mce-EMAIL" placeholder="email address" required />

<input type="text" name="b_f8c1f1dae22f9657c634ea871_163fce62d4" tabindex="-1" />

	    <input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button" />

	</form>

Release Terms

Our entire catalogue is free for download.

Our releases are available in several free distribution platforms such as Scene.Org, Internet Archive, Sonic Squirrel, Free Music Archive, etc.

Our releases are also available on a few commercial distribution and online radio platforms such as Last FM, Bandcamp, Spotify, iTunes, Amazon, Google Play, etc. Some of these platforms force us to put a price tag on our releases, we try to always set the possible minimum required. Any income from such platforms is used on promotional material for the label as described on our activity reports of 2012 and 2013. We operate non-profit.

Our releases are free of any DRM and have associated Creative Commons BY-NC-SA 4.0 licenses (unless otherwise specified). Feel free to share, use and remix accordingly.

Demo Policy

We are closed to demos from new artists. We are trying to focus more on promoting our current (and very long) roster of artists. That doesn't mean it's impossible to debut a new artist on Enough, just highly unlikely.

Sub Labels

Catita!, tagged as <Tag tag="c!">c!</Tag> in our database. It groups the 4 releases salvaged from digital oblivion as the Catita! netlabel stopped its activities. They focused on 8bit music.

Anonymous Archives, tagged as <Tag tag="Anon">Anon</Tag>. Our socio-political activist sub-label.

[Esc.] Laboratory, tagged as <Tag tag="[Esc.]">[Esc.]</Tag>. This sub-label groups the releases that are related to the [Esc.] Laboratory collective from Germany.

Thisko, tagged as <Tag tag="thisk">thisk</Tag>. This sub-label groups the releases related or co-released with our friends from Thisco Records.

News

We are present on all major social networks (facebook, twitter and google plus).

Contact

Any further inquiries about Enough Records please contact Filipe 'ps' Cruz by email.

Acknowledgements

Founded in 2001 by Fred, H4rv3st and ps.

Our thanks go out to all the folks who helped improve, run and promote Enough Records throughout the years. You are too many to list without risk of forgetting someone important. You know who you are, thank you, Enough Records would not be what it is without you.

Current website developed by Dan Peddle and ps as an open source project, feel free to fork, reuse and contribute.

		);
	}
});

module.exports = About;
<commit_msg>Update title on about page<commit_after>/**
 * @jsx React.DOM
 */

var React = require('react');

var Router = require('react-router');
var Link = Router.Link;

var Tag = require('./tag');

var About = React.createClass({
	componentWillMount: function(){
		document.title = 'About';
	},
	render: function(){
		return (

About

Enough Records is a netlabel (wikipedia link). We been active since 2001 and have no focus on any specific genre.

Mailing List

Subscribe to our announcemente mailing list

	<form action="//scene.us3.list-manage.com/subscribe/post?u=f8c1f1dae22f9657c634ea871&id=163fce62d4" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>

		<input type="email" name="EMAIL" class="email" id="mce-EMAIL" placeholder="email address" required />

<input type="text" name="b_f8c1f1dae22f9657c634ea871_163fce62d4" tabindex="-1" />

	    <input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button" />

	</form>

Release Terms

Our entire catalogue is free for download.

Our releases are available in several free distribution platforms such as Scene.Org, Internet Archive, Sonic Squirrel, Free Music Archive, etc.

Our releases are also available on a few commercial distribution and online radio platforms such as Last FM, Bandcamp, Spotify, iTunes, Amazon, Google Play, etc. Some of these platforms force us to put a price tag on our releases, we try to always set the possible minimum required. Any income from such platforms is used on promotional material for the label as described on our activity reports of 2012 and 2013. We operate non-profit.

Our releases are free of any DRM and have associated Creative Commons BY-NC-SA 4.0 licenses (unless otherwise specified). Feel free to share, use and remix accordingly.

Demo Policy

We are closed to demos from new artists. We are trying to focus more on promoting our current (and very long) roster of artists. That doesn't mean it's impossible to debut a new artist on Enough, just highly unlikely.

Sub Labels

Catita!, tagged as <Tag tag="c!">c!</Tag> in our database. It groups the 4 releases salvaged from digital oblivion as the Catita! netlabel stopped its activities. They focused on 8bit music.

Anonymous Archives, tagged as <Tag tag="Anon">Anon</Tag>. Our socio-political activist sub-label.

[Esc.] Laboratory, tagged as <Tag tag="[Esc.]">[Esc.]</Tag>. This sub-label groups the releases that are related to the [Esc.] Laboratory collective from Germany.

Thisko, tagged as <Tag tag="thisk">thisk</Tag>. This sub-label groups the releases related or co-released with our friends from Thisco Records.

News

We are present on all major social networks (facebook, twitter and google plus).

Contact

Any further inquiries about Enough Records please contact Filipe 'ps' Cruz by email.

Acknowledgements

Founded in 2001 by Fred, H4rv3st and ps.

Our thanks go out to all the folks who helped improve, run and promote Enough Records throughout the years. You are too many to list without risk of forgetting someone important. You know who you are, thank you, Enough Records would not be what it is without you.

Current website developed by Dan Peddle and ps as an open source project, feel free to fork, reuse and contribute.

		);
	}
});

module.exports = About;
<|endoftext|>


<commit_before>Parse.Cloud.define("newNotifications", function(request, response) {
	console.log("newNotifications: " + JSON.stringify(request.params));
	var userId = request.user.id;
	var query = new Parse.Query('Notification');
	query.equalTo("to", userId);
	//query.greaterThan("createdAt", new Date(request.params.lastTime.iso));
	query.limit = 1;
	query.find({
		success: function(results) {
			if (results.length > 0) {
				console.log("Notification test: user=" + request.user.id + " status=" + results.length);
				response.success(true)
			}
			else {
				// now count logged views if there are no notifications
				var query = new Parse.Query('Log');
				query.equalTo("to", userId);
				query.greaterThan("createdAt", new Date(request.params.lastTime.iso));
				query.limit = 1;
				query.find({
					success: function(logResults) {
						console.log("Notification Log test: user=" + request.user.id + " status=" + logResults.length);
						response.success(logResults.length > 0);
					},
					error: function(error) {
						console.error(error);
						response.error(error);
					}
				});
			}
		},
		error: function(error) {
			console.error(error);
			response.error(error);
		}
	});
});
<commit_msg>Notification logging<commit_after>Parse.Cloud.define("newNotifications", function(request, response) {
	console.log("newNotifications: " + JSON.stringify(request.params));
	var userId = request.user.id;
	var query = new Parse.Query(Parse.Object.extend('Notification'));
	query.equalTo("to", userId);
	query.greaterThan("createdAt", new Date(request.params.lastTime.iso));
	query.limit = 1;
	query.find({
		success: function(results) {
			if (results.length > 0) {
				console.log("Notification test: user=" + request.user.id + " status=" + results.length);
				response.success(true)
			}
			else {
				// now count logged views if there are no notifications
				var query = new Parse.Query('Log');
				query.equalTo("to", userId);
				query.greaterThan("createdAt", new Date(request.params.lastTime.iso));
				query.limit = 1;
				query.find({
					success: function(logResults) {
						console.log("Notification Log test: user=" + request.user.id + " status=" + logResults.length);
						response.success(logResults.length > 0);
					},
					error: function(error) {
						console.error(error);
						response.error(error);
					}
				});
			}
		},
		error: function(error) {
			console.error(error);
			response.error(error);
		}
	});
});
<|endoftext|>


<commit_before>const path = require('path'),
    ExtractTextPlugin = require("extract-text-webpack-plugin"),
    HtmlWebpackPlugin = require('html-webpack-plugin'),
    ManifestPlugin = require('webpack-manifest-plugin'),
    config = require('./config.js');

path.joinFormat = function(){
    const iArgv = Array.prototype.slice.call(arguments);
    const r = path.join.apply(path, iArgv);
    return r
        .replace(/\\+/g, '/')
        .replace(/(^http[s]?:)[\/]+/g, '$1//');
};

module.exports = {
    devServer:{
        host: '127.0.0.1',
        progress: true,
        colors: true,
        contentBase: config.deployserver.root,
        port: 5000,
        // hot: true,
        // inline: true

    },
    entry: {
        'index': './src/static/js/index.js'
    },
    output: {
        path: path.join(__dirname, config.path.jsDest),
        publicPath: path.joinFormat(
            config.deployserver.path,
            path.relative(
                path.join(
                    config.deployserver.root,
                    config.deployserver.path
                ),
                config.path.jsDest
            ),
            '/'
        ),
        filename: '[name]-[chunkhash:8].js'
    },
    module: {
        loaders: [{
            test: /\.js$/,
            exclude: '/node_modules/',
            loader: 'babel-loader'

        }, {
            test: /\.vue$/,
            loaders: ['vue']
        }, {
            test: /\.scss$/,
            loader: ExtractTextPlugin.extract("style-loader", "css!sass")
        }, {
            test: /\.jade$/,
            loaders: ['jade-loader']
        }, {
            test: /\.(png|jpg|gif)$/,
            loader: 'url?limit=10000&name=../images/[name]-[hash:8].[ext]'
        }, {
            // shiming the module
            test: path.join(__dirname, 'src/static/js/lib/'),
            loader: 'imports?this=>window'
        }]

    },
    resolveLoader: {
        fallback: path.join(__dirname, "node_modules")
    },
    resolve: {
        fallback: path.join(__dirname, "node_modules"),
        root: './',
        alias: {
        }
    },
    devtool: 'source-map',
    plugins: [
        // 样式分离插件
        new ExtractTextPlugin("../css/index-[chunkhash:8].css"),

        // html输出插件
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/static/jade/index.jade'),
            filename: '../html/index.html',
            minify: false
        }),
        new ManifestPlugin({
            fileName: '../assets/rev-manifest.json',
            basePath: ''

        })
    ]

};
<commit_msg>[EDIT] 修改manifest路径<commit_after>const path = require('path'),
    ExtractTextPlugin = require("extract-text-webpack-plugin"),
    HtmlWebpackPlugin = require('html-webpack-plugin'),
    ManifestPlugin = require('webpack-manifest-plugin'),
    config = require('./config.js');

path.joinFormat = function(){
    const iArgv = Array.prototype.slice.call(arguments);
    const r = path.join.apply(path, iArgv);
    return r
        .replace(/\\+/g, '/')
        .replace(/(^http[s]?:)[\/]+/g, '$1//');
};
path.component = 'src/static/component';

module.exports = {
    devServer:{
        host: '127.0.0.1',
        progress: true,
        colors: true,
        contentBase: config.deployserver.root,
        port: 5000,
        // hot: true,
        // inline: true

    },
    entry: {
        'index': './src/static/js/index.js'
    },
    output: {
        path: path.join(__dirname, config.path.jsDest),
        publicPath: path.joinFormat(
            config.deployserver.path,
            path.relative(
                path.join(
                    config.deployserver.root,
                    config.deployserver.path
                ),
                config.path.jsDest
            ),
            '/'
        ),
        filename: '[name]-[chunkhash:8].js'
    },
    module: {
        loaders: [{
            test: /\.js$/,
            exclude: '/node_modules/',
            loader: 'babel-loader'

        }, {
            test: /\.vue$/,
            loaders: ['vue']
        }, {
            test: /\.scss$/,
            loader: ExtractTextPlugin.extract("style-loader", "css!sass")
        }, {
            test: /\.jade$/,
            loaders: ['jade-loader']
        }, {
            test: /\.(png|jpg|gif)$/,
            loader: 'url?limit=10000&name=../images/[name]-[hash:8].[ext]'
        }, {
            // shiming the module
            test: path.join(__dirname, 'src/static/js/lib/'),
            loader: 'imports?this=>window'
        }]

    },
    resolveLoader: {
        fallback: path.join(__dirname, "node_modules")
    },
    resolve: {
        fallback: path.join(__dirname, "node_modules"),
        root: './',
        alias: {
            'header': path.join(__dirname, path.component, 'header/header.vue'),
            'editor': path.join(__dirname, path.component, 'editor/editor.vue'),
            'timeTable': path.join(__dirname, path.component, 'timeTable/timeTable.vue'),
            'footer': path.join(__dirname, path.component, 'footer/footer.vue'),
            'loginPanel': path.join(__dirname, path.component, 'loginPanel/loginPanel.vue'),
        }
    },
    devtool: 'source-map',
    plugins: [
        // 样式分离插件
        new ExtractTextPlugin("../css/index-[chunkhash:8].css"),

        // html输出插件
        new HtmlWebpackPlugin({
            template: path.join(__dirname, 'src/static/jade/index.jade'),
            filename: '../html/index.html',
            minify: false
        }),
        new ManifestPlugin({
            fileName: '../../../dist/server/assets/rev-manifest.json',
            basePath: ''

        })
    ]

};
<|endoftext|>


<commit_before>/**
 * Created by Ibby on 3/20/2017.
 */
"use strict";

const projectPackage = require('../package.json'),
  Dgeni = require('dgeni'),
  dgeniConfig = require('./dgeni_config')(projectPackage.version);

const dgeni = new Dgeni([dgeniConfig]);

  dgeni.generate()
  .then(docs => {
    console.log(`${docs.length} docs generated`);
  })
  .catch(e => console.log('Error generating docs', e));
<commit_msg>output error if doc generation fails<commit_after>/**
 * Created by Ibby on 3/20/2017.
 */
"use strict";

const projectPackage = require('../package.json'),
  Dgeni = require('dgeni'),
  dgeniConfig = require('./dgeni_config')(projectPackage.version);

const dgeni = new Dgeni([dgeniConfig]);

try {
  dgeni.generate()
    .then(docs => {
      console.log(`${docs.length} docs generated`);
    });
} catch (e) {
  console.error('An error occurred while attempting to generate docs');
  console.error(e.message);
  console.error(e.stack);
  process.exit(1);
}
<|endoftext|>


<commit_before>//
//  Backbone-associations.js 0.4.2
//
//  (c) 2013 Dhruva Ray, Jaynti Kanani, Persistent Systems Ltd.
//  Backbone-associations may be freely distributed under the MIT license.
//  For all details and documentation:
//  https://github.com/dhruvaray/backbone-associations/
//

// Initial Setup
// --------------
(function () {
    "use strict";

    // Save a reference to the global object (`window` in the browser, `exports`
    // on the server).
    var root = this;

    // The top-level namespace. All public Backbone classes and modules will be attached to this.
    // Exported for the browser and CommonJS.
    var _, Backbone, BackboneModel, BackboneCollection, ModelProto,
        CollectionProto, defaultEvents, AssociatedModel, pathChecker;

    if (typeof window === 'undefined') {
        _ = require('underscore');
        Backbone = require('backbone');
        if (typeof exports !== 'undefined') {
            exports = module.exports = Backbone;
        }
    } else {
        _ = root._;
        Backbone = root.Backbone;
    }
    // Create local reference `Model` prototype.
    BackboneModel = Backbone.Model;
    BackboneCollection = Backbone.Collection;
    ModelProto = BackboneModel.prototype;
    CollectionProto = BackboneCollection.prototype;
    pathChecker = /[\.\[\]]+/g;

    // Built-in Backbone `events`.
    defaultEvents = ["change", "add", "remove", "reset", "destroy",
        "sync", "error", "sort", "request"];

    // Backbone.AssociatedModel
    // --------------

    //Add `Many` and `One` relations to Backbone Object.
    Backbone.Many = "Many";
    Backbone.One = "One";
    // Define `AssociatedModel` (Extends Backbone.Model).
    AssociatedModel = Backbone.AssociatedModel = BackboneModel.extend({
        // Define relations with Associated Model.
        relations:undefined,
        // Define `Model` property which can keep track of already fired `events`,
        // and prevent redundant event to be triggered in case of cyclic model graphs.
        _proxyCalls:undefined,

        // Get the value of an attribute.
        get:function (attr) {
            var obj = ModelProto.get.call(this, attr);
            return obj ? obj : this.getAttr.apply(this, arguments);
        },

        // Set a hash of model attributes on the Backbone Model.
        set:function (key, value, options) {
            var attributes, attr, modelMap, modelId, obj, result = this;
            // Duplicate backbone's behavior to allow separate key/value parameters,
            // instead of a single 'attributes' object.
            if (_.isObject(key) || key == null) {
                attributes = key;
                options = value;
            } else {
                attributes = {};
                attributes[key] = value;
            }
            if (!attributes) return this;
            for (attr in attributes) {
                //Create a map for each unique object whose attributes we want to set
                modelMap || (modelMap = {});
                if (attr.match(pathChecker)) {
                    var pathTokens = getPathArray(attr), initials = _.initial(pathTokens),
                        last = pathTokens[pathTokens.length - 1],
                        parentModel = this.get(initials);
                    if (parentModel instanceof AssociatedModel) {
                        obj = modelMap[parentModel.cid] || (modelMap[parentModel.cid] = {'model':parentModel, 'data':{}});
                        obj.data[last] = attributes[attr];
                    }
                } else {
                    obj = modelMap[this.cid] || (modelMap[this.cid] = {'model':this, 'data':{}});
                    obj.data[attr] = attributes[attr];
                }
            }
            if (modelMap) {
                for (modelId in modelMap) {
                    obj = modelMap[modelId];
                    this.setAttr.call(obj.model, obj.data, options) || (result = false);
                }
            } else {
                return this.setAttr.call(this, attributes, options);
            }
            return result;
        },

        // Set a hash of model attributes on the object,
        // fire Backbone `event` with options.
        // It maintains relations between models during the set operation.
        // It also bubbles up child events to the parent.
        setAttr:function (attributes, options) {
            var attr;
            // Extract attributes and options.
            options || (options = {});
            if (options.unset) for (attr in attributes) attributes[attr] = void 0;
            this.parents = this.parents || [];

            if (this.relations) {
                // Iterate over `this.relations` and `set` model and collection values
                // if `relations` are available.
                _.each(this.relations, function (relation) {
                    var relationKey = relation.key, relatedModel = relation.relatedModel,
                        collectionType = relation.collectionType,
                        map = relation.map,
                        val, relationOptions, data, relationValue;

                    //Get class if relation and map is stored as a string.
                    relatedModel && _.isString(relatedModel) && (relatedModel = eval(relatedModel));
                    collectionType && _.isString(collectionType) && (collectionType = eval(collectionType));
                    map && _.isString(map) && (map = eval(map));
                    // Merge in `options` specific to this relation.
                    relationOptions = relation.options ? _.extend({}, relation.options, options) : options;

                    if (attributes[relationKey]) {
                        //Get value of attribute with relation key in `val`.
                        val = _.result(attributes, relationKey);
                        //Map `val` if a transformation function is provided.
                        val = map ? map(val) : val;

                        // If `relation.type` is `Backbone.Many`,
                        // create `Backbone.Collection` with passed data and perform Backbone `set`.
                        if (relation.type === Backbone.Many) {
                            // `collectionType` of defined `relation` should be instance of `Backbone.Collection`.
                            if (collectionType && !collectionType.prototype instanceof BackboneCollection) {
                                throw new Error('collectionType must inherit from Backbone.Collection');
                            }

                            if (val instanceof BackboneCollection) {
                                data = val;
                                attributes[relationKey] = data;
                            } else {
                                data = collectionType ? new collectionType() : this._createCollection(relatedModel);
                                data.add(val, relationOptions);
                                attributes[relationKey] = data;
                            }

                        } else if (relation.type === Backbone.One && relatedModel) {
                            data = val instanceof AssociatedModel ? val : new relatedModel(val, relationOptions);
                            attributes[relationKey] = data;
                        }

                        relationValue = data;

                        // Add proxy events to respective parents.
                        // Only add callback if not defined.
                        if (relationValue && !relationValue._proxyCallback) {
                            relationValue._proxyCallback = function () {
                                return this._bubbleEvent.call(this, relationKey, relationValue, arguments);
                            };
                            relationValue.on("all", relationValue._proxyCallback, this);
                        }

                    }
                    //Distinguish between the value of undefined versus a set no-op
                    if (attributes.hasOwnProperty(relationKey)) {
                        //Maintain reverse pointers - a.k.a parents
                        var updated = attributes[relationKey];
                        var original = this.attributes[relationKey];
                        if (updated) {
                            updated.parents = updated.parents || [];
                            (updated.parents.indexOf(this) == -1) && updated.parents.push(this);
                        } else if (original && original.parents.length > 0) {
                            original.parents = _.difference(original.parents, [this]);
                        }
                    }
                }, this);
            }
            // Return results for `BackboneModel.set`.
            return ModelProto.set.call(this, attributes, options);
        },
        // Bubble-up event to `parent` Model
        _bubbleEvent:function (relationKey, relationValue, eventArguments) {
            var args = eventArguments,
                opt = args[0].split(":"),
                eventType = opt[0],
                eventObject = args[1],
                options = args.length > 0 && args[args.length - 1],
                simulated = options.simulated,
                simulatedChange = "nested-change" == eventType,
                indexEventObject = -1,
                _proxyCalls = relationValue._proxyCalls,
                eventPath,
                basecolEventPath,
                triggerCollectionEvent = false;
            // Change the event name to a fully qualified path.
            _.size(opt) > 1 && (eventPath = opt[1]);

            //Don't bubble `nested-change` events
            if (simulatedChange) return;

            // Find the specific object in the collection which has changed.
            if (relationValue instanceof BackboneCollection && "change" === eventType && eventObject && !simulated) {
                var pathTokens = getPathArray(eventPath),
                    initialTokens = _.initial(pathTokens), colModel;

                colModel = relationValue.find(function (model) {
                    if (eventObject === model) return true;
                    if (model) {
                        var changedModel = model.get(initialTokens);
                        if ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection) &&
                            eventObject === changedModel) return true;
                        changedModel = model.get(pathTokens);
                        return ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection)
                            && eventObject === changedModel);
                    }
                    return false;
                });
                colModel && (indexEventObject = relationValue.indexOf(colModel));
            }

            triggerCollectionEvent = ("change" == eventType && !eventPath && indexEventObject !== -1);

            // Manipulate `eventPath`.
            eventPath = relationKey + (indexEventObject !== -1 ?
                "[" + indexEventObject + "]" : "") + (eventPath ? "." + eventPath : "");
            args[0] = eventType + ":" + eventPath;

            // If event has been already triggered as result of same source `eventPath`,
            // no need to re-trigger event to prevent cycle.
            if (_proxyCalls) {
                if (this._isEventAvailable.call(this, _proxyCalls, eventPath)) return this;
            } else {
                _proxyCalls = relationValue._proxyCalls = {};
            }
            // Add `eventPath` in `_proxyCalls` to keep track of already triggered `event`.
            _proxyCalls[eventPath] = true;

            //Set up previous attributes correctly.
            if ("change" === eventType) {
                this._previousAttributes[relationKey] = relationValue._previousAttributes;
                this.changed[relationKey] = relationValue;
            }

            // Bubble up event to parent `model` with new changed arguments.
            this.trigger.apply(this, args);

            // Remove `eventPath` from `_proxyCalls`,
            // if `eventPath` and `_proxyCalls` are available,
            // which allow event to be triggered on for next operation of `set`.
            if (_proxyCalls && eventPath) delete _proxyCalls[eventPath];

            //Simulate events to ease application programming with an object graph
            //1. Create a nested-change event
            if (!simulated && this._fireNestedChange(eventType, eventPath, args[2])) {
                var ncargs = [];
                ncargs.push("nested-change");
                ncargs.push(relationKey);
                ncargs.push.apply(ncargs, args);
                ncargs.push({simulated:true});
                this.trigger.apply(this, ncargs);
            }
            //2. Create a collection modified event without specifying the item in the collection which was modified
            if (triggerCollectionEvent) {
                basecolEventPath = relationKey;
                if (!this._isEventAvailable.call(this, _proxyCalls, basecolEventPath)) {
                    var ccargs = [];
                    ccargs.push.apply(ccargs, args);
                    ccargs.push({simulated:true});
                    ccargs[0] = eventType + ":" + basecolEventPath;

                    _proxyCalls[basecolEventPath] = true;
                    this.trigger.apply(this, ccargs);
                    if (_proxyCalls && basecolEventPath) delete _proxyCalls[basecolEventPath];

                }

            }

            return this;
        },

        //Determine whether to fire a nested-change event for grand-parents and higher objects
        _fireNestedChange:function (eventType, fqpn, value) {
            return (
                ("change" !== eventType) ||
                    (this.getAttr.call(this, fqpn) != value) //Not a change:attribute event
                );
        },

        _isEventAvailable:function (_proxyCalls, path) {
            return _.find(_proxyCalls, function (value, eventKey) {
                return path.indexOf(eventKey, path.length - eventKey.length) !== -1;
            });
        },

        // Returns New `collection` of type `relation.relatedModel`.
        _createCollection:function (type) {
            var collection, relatedModel = type;
            _.isString(relatedModel) && (relatedModel = eval(relatedModel));
            // Creates new `Backbone.Collection` and defines model class.
            if (relatedModel && relatedModel.prototype instanceof AssociatedModel) {
                collection = new BackboneCollection();
                collection.model = relatedModel;
            } else {
                throw new Error('type must inherit from Backbone.AssociatedModel');
            }
            return collection;
        },
        // The JSON representation of the model.
        toJSON:function (options) {
            var json, aJson;
            if (!this.visited) {
                this.visited = true;
                // Get json representation from `BackboneModel.toJSON`.
                json = ModelProto.toJSON.apply(this, arguments);
                // If `this.relations` is defined, iterate through each `relation`
                // and added it's json representation to parents' json representation.
                if (this.relations) {
                    _.each(this.relations, function (relation) {
                        var attr = this.attributes[relation.key];
                        if (attr) {
                            aJson = attr.toJSON(options);
                            json[relation.key] = _.isArray(aJson) ? _.compact(aJson) : aJson;
                        }
                    }, this);
                }
                delete this.visited;
            }
            return json;
        },

        // Create a new model with identical attributes to this one.
        clone:function () {
            return new this.constructor(this.toJSON());
        },

        //Navigate the path to the leaf object in the path to query for the attribute value
        getAttr:function (path) {

            var result = this,
            //Tokenize the path
                attrs = getPathArray(path),
                key,
                i;
            if (_.size(attrs) < 1) return;
            for (i = 0; i < attrs.length; i++) {
                key = attrs[i];
                if (!result) break;
                //Navigate the path to get to the result
                result = result instanceof BackboneCollection && (!isNaN(key)) ? result.at(key) : result.attributes[key];
            }
            return result;
        }
    });

    var delimiters = /[^\.\[\]]+/g;

    // Tokenize the fully qualified event path
    var getPathArray = function (path) {
        if (path === '') return [''];
        return _.isString(path) ? (path.match(delimiters)) : path || [];
    };

    //Infer the relation from the collection's parents and find the appropriate map for the passed in `models`
    var map2models = function (parents, target, models) {
        var relation;
        //Iterate over collection's parents
        _.find(parents, function (parent) {
            //Iterate over relations
            relation = _.find(parent.relations, function (rel) {
                return parent.get(rel.key) === target;
            }, this);
            if (relation) return true;//break;
        }, this);

        //If we found a relation and it has a mapping function
        if (relation && relation.map) {
            return relation.map(models)
        }
        return models;
    };

    var proxies = {};
    // Proxy Backbone collection methods
    _.each(['set', 'remove', 'reset'], function (method) {
        proxies[method] = BackboneCollection.prototype[method];

        CollectionProto[method] = function (models, options) {
            //Short-circuit if this collection doesn't hold `AssociatedModels`
            if (this.model.prototype instanceof AssociatedModel && this.parents) {
                //Find a map function if available and perform a transformation
                arguments[0] = map2models(this.parents, this, models);
            }
            return proxies[method].apply(this, arguments);
        }
    });

    // Version
    AssociatedModel.VERSION = "0.4.2";

}).call(this);
<commit_msg>Fix for issue #42 (IE8 support)<commit_after>//
//  Backbone-associations.js 0.4.2
//
//  (c) 2013 Dhruva Ray, Jaynti Kanani, Persistent Systems Ltd.
//  Backbone-associations may be freely distributed under the MIT license.
//  For all details and documentation:
//  https://github.com/dhruvaray/backbone-associations/
//

// Initial Setup
// --------------
(function () {
    "use strict";

    // Save a reference to the global object (`window` in the browser, `exports`
    // on the server).
    var root = this;

    // The top-level namespace. All public Backbone classes and modules will be attached to this.
    // Exported for the browser and CommonJS.
    var _, Backbone, BackboneModel, BackboneCollection, ModelProto,
        CollectionProto, defaultEvents, AssociatedModel, pathChecker;

    if (typeof window === 'undefined') {
        _ = require('underscore');
        Backbone = require('backbone');
        if (typeof exports !== 'undefined') {
            exports = module.exports = Backbone;
        }
    } else {
        _ = root._;
        Backbone = root.Backbone;
    }
    // Create local reference `Model` prototype.
    BackboneModel = Backbone.Model;
    BackboneCollection = Backbone.Collection;
    ModelProto = BackboneModel.prototype;
    CollectionProto = BackboneCollection.prototype;
    pathChecker = /[\.\[\]]+/g;

    // Built-in Backbone `events`.
    defaultEvents = ["change", "add", "remove", "reset", "destroy",
        "sync", "error", "sort", "request"];

    // Backbone.AssociatedModel
    // --------------

    //Add `Many` and `One` relations to Backbone Object.
    Backbone.Many = "Many";
    Backbone.One = "One";
    // Define `AssociatedModel` (Extends Backbone.Model).
    AssociatedModel = Backbone.AssociatedModel = BackboneModel.extend({
        // Define relations with Associated Model.
        relations:undefined,
        // Define `Model` property which can keep track of already fired `events`,
        // and prevent redundant event to be triggered in case of cyclic model graphs.
        _proxyCalls:undefined,

        // Get the value of an attribute.
        get:function (attr) {
            var obj = ModelProto.get.call(this, attr);
            return obj ? obj : this.getAttr.apply(this, arguments);
        },

        // Set a hash of model attributes on the Backbone Model.
        set:function (key, value, options) {
            var attributes, attr, modelMap, modelId, obj, result = this;
            // Duplicate backbone's behavior to allow separate key/value parameters,
            // instead of a single 'attributes' object.
            if (_.isObject(key) || key == null) {
                attributes = key;
                options = value;
            } else {
                attributes = {};
                attributes[key] = value;
            }
            if (!attributes) return this;
            for (attr in attributes) {
                //Create a map for each unique object whose attributes we want to set
                modelMap || (modelMap = {});
                if (attr.match(pathChecker)) {
                    var pathTokens = getPathArray(attr), initials = _.initial(pathTokens),
                        last = pathTokens[pathTokens.length - 1],
                        parentModel = this.get(initials);
                    if (parentModel instanceof AssociatedModel) {
                        obj = modelMap[parentModel.cid] || (modelMap[parentModel.cid] = {'model':parentModel, 'data':{}});
                        obj.data[last] = attributes[attr];
                    }
                } else {
                    obj = modelMap[this.cid] || (modelMap[this.cid] = {'model':this, 'data':{}});
                    obj.data[attr] = attributes[attr];
                }
            }
            if (modelMap) {
                for (modelId in modelMap) {
                    obj = modelMap[modelId];
                    this.setAttr.call(obj.model, obj.data, options) || (result = false);
                }
            } else {
                return this.setAttr.call(this, attributes, options);
            }
            return result;
        },

        // Set a hash of model attributes on the object,
        // fire Backbone `event` with options.
        // It maintains relations between models during the set operation.
        // It also bubbles up child events to the parent.
        setAttr:function (attributes, options) {
            var attr;
            // Extract attributes and options.
            options || (options = {});
            if (options.unset) for (attr in attributes) attributes[attr] = void 0;
            this.parents = this.parents || [];

            if (this.relations) {
                // Iterate over `this.relations` and `set` model and collection values
                // if `relations` are available.
                _.each(this.relations, function (relation) {
                    var relationKey = relation.key, relatedModel = relation.relatedModel,
                        collectionType = relation.collectionType,
                        map = relation.map,
                        val, relationOptions, data, relationValue;

                    //Get class if relation and map is stored as a string.
                    relatedModel && _.isString(relatedModel) && (relatedModel = eval(relatedModel));
                    collectionType && _.isString(collectionType) && (collectionType = eval(collectionType));
                    map && _.isString(map) && (map = eval(map));
                    // Merge in `options` specific to this relation.
                    relationOptions = relation.options ? _.extend({}, relation.options, options) : options;

                    if (attributes[relationKey]) {
                        //Get value of attribute with relation key in `val`.
                        val = _.result(attributes, relationKey);
                        //Map `val` if a transformation function is provided.
                        val = map ? map(val) : val;

                        // If `relation.type` is `Backbone.Many`,
                        // create `Backbone.Collection` with passed data and perform Backbone `set`.
                        if (relation.type === Backbone.Many) {
                            // `collectionType` of defined `relation` should be instance of `Backbone.Collection`.
                            if (collectionType && !collectionType.prototype instanceof BackboneCollection) {
                                throw new Error('collectionType must inherit from Backbone.Collection');
                            }

                            if (val instanceof BackboneCollection) {
                                data = val;
                                attributes[relationKey] = data;
                            } else {
                                data = collectionType ? new collectionType() : this._createCollection(relatedModel);
                                data.add(val, relationOptions);
                                attributes[relationKey] = data;
                            }

                        } else if (relation.type === Backbone.One && relatedModel) {
                            data = val instanceof AssociatedModel ? val : new relatedModel(val, relationOptions);
                            attributes[relationKey] = data;
                        }

                        relationValue = data;

                        // Add proxy events to respective parents.
                        // Only add callback if not defined.
                        if (relationValue && !relationValue._proxyCallback) {
                            relationValue._proxyCallback = function () {
                                return this._bubbleEvent.call(this, relationKey, relationValue, arguments);
                            };
                            relationValue.on("all", relationValue._proxyCallback, this);
                        }

                    }
                    //Distinguish between the value of undefined versus a set no-op
                    if (attributes.hasOwnProperty(relationKey)) {
                        //Maintain reverse pointers - a.k.a parents
                        var updated = attributes[relationKey];
                        var original = this.attributes[relationKey];
                        if (updated) {
                            updated.parents = updated.parents || [];
                            (_.indexOf(updated.parents, this) == -1) && updated.parents.push(this);
                        } else if (original && original.parents.length > 0) {
                            original.parents = _.difference(original.parents, [this]);
                        }
                    }
                }, this);
            }
            // Return results for `BackboneModel.set`.
            return ModelProto.set.call(this, attributes, options);
        },
        // Bubble-up event to `parent` Model
        _bubbleEvent:function (relationKey, relationValue, eventArguments) {
            var args = eventArguments,
                opt = args[0].split(":"),
                eventType = opt[0],
                eventObject = args[1],
                options = args.length > 0 && args[args.length - 1],
                simulated = options.simulated,
                simulatedChange = "nested-change" == eventType,
                indexEventObject = -1,
                _proxyCalls = relationValue._proxyCalls,
                eventPath,
                basecolEventPath,
                triggerCollectionEvent = false;
            // Change the event name to a fully qualified path.
            _.size(opt) > 1 && (eventPath = opt[1]);

            //Don't bubble `nested-change` events
            if (simulatedChange) return;

            // Find the specific object in the collection which has changed.
            if (relationValue instanceof BackboneCollection && "change" === eventType && eventObject && !simulated) {
                var pathTokens = getPathArray(eventPath),
                    initialTokens = _.initial(pathTokens), colModel;

                colModel = relationValue.find(function (model) {
                    if (eventObject === model) return true;
                    if (model) {
                        var changedModel = model.get(initialTokens);
                        if ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection) &&
                            eventObject === changedModel) return true;
                        changedModel = model.get(pathTokens);
                        return ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection)
                            && eventObject === changedModel);
                    }
                    return false;
                });
                colModel && (indexEventObject = relationValue.indexOf(colModel));
            }

            triggerCollectionEvent = ("change" == eventType && !eventPath && indexEventObject !== -1);

            // Manipulate `eventPath`.
            eventPath = relationKey + (indexEventObject !== -1 ?
                "[" + indexEventObject + "]" : "") + (eventPath ? "." + eventPath : "");
            args[0] = eventType + ":" + eventPath;

            // If event has been already triggered as result of same source `eventPath`,
            // no need to re-trigger event to prevent cycle.
            if (_proxyCalls) {
                if (this._isEventAvailable.call(this, _proxyCalls, eventPath)) return this;
            } else {
                _proxyCalls = relationValue._proxyCalls = {};
            }
            // Add `eventPath` in `_proxyCalls` to keep track of already triggered `event`.
            _proxyCalls[eventPath] = true;

            //Set up previous attributes correctly.
            if ("change" === eventType) {
                this._previousAttributes[relationKey] = relationValue._previousAttributes;
                this.changed[relationKey] = relationValue;
            }

            // Bubble up event to parent `model` with new changed arguments.
            this.trigger.apply(this, args);

            // Remove `eventPath` from `_proxyCalls`,
            // if `eventPath` and `_proxyCalls` are available,
            // which allow event to be triggered on for next operation of `set`.
            if (_proxyCalls && eventPath) delete _proxyCalls[eventPath];

            //Simulate events to ease application programming with an object graph
            //1. Create a nested-change event
            if (!simulated && this._fireNestedChange(eventType, eventPath, args[2])) {
                var ncargs = [];
                ncargs.push("nested-change");
                ncargs.push(relationKey);
                ncargs.push.apply(ncargs, args);
                ncargs.push({simulated:true});
                this.trigger.apply(this, ncargs);
            }
            //2. Create a collection modified event without specifying the item in the collection which was modified
            if (triggerCollectionEvent) {
                basecolEventPath = relationKey;
                if (!this._isEventAvailable.call(this, _proxyCalls, basecolEventPath)) {
                    var ccargs = [];
                    ccargs.push.apply(ccargs, args);
                    ccargs.push({simulated:true});
                    ccargs[0] = eventType + ":" + basecolEventPath;

                    _proxyCalls[basecolEventPath] = true;
                    this.trigger.apply(this, ccargs);
                    if (_proxyCalls && basecolEventPath) delete _proxyCalls[basecolEventPath];

                }

            }

            return this;
        },

        //Determine whether to fire a nested-change event for grand-parents and higher objects
        _fireNestedChange:function (eventType, fqpn, value) {
            return (
                ("change" !== eventType) ||
                    (this.getAttr.call(this, fqpn) != value) //Not a change:attribute event
                );
        },

        _isEventAvailable:function (_proxyCalls, path) {
            return _.find(_proxyCalls, function (value, eventKey) {
                return path.indexOf(eventKey, path.length - eventKey.length) !== -1;
            });
        },

        // Returns New `collection` of type `relation.relatedModel`.
        _createCollection:function (type) {
            var collection, relatedModel = type;
            _.isString(relatedModel) && (relatedModel = eval(relatedModel));
            // Creates new `Backbone.Collection` and defines model class.
            if (relatedModel && relatedModel.prototype instanceof AssociatedModel) {
                collection = new BackboneCollection();
                collection.model = relatedModel;
            } else {
                throw new Error('type must inherit from Backbone.AssociatedModel');
            }
            return collection;
        },
        // The JSON representation of the model.
        toJSON:function (options) {
            var json, aJson;
            if (!this.visited) {
                this.visited = true;
                // Get json representation from `BackboneModel.toJSON`.
                json = ModelProto.toJSON.apply(this, arguments);
                // If `this.relations` is defined, iterate through each `relation`
                // and added it's json representation to parents' json representation.
                if (this.relations) {
                    _.each(this.relations, function (relation) {
                        var attr = this.attributes[relation.key];
                        if (attr) {
                            aJson = attr.toJSON(options);
                            json[relation.key] = _.isArray(aJson) ? _.compact(aJson) : aJson;
                        }
                    }, this);
                }
                delete this.visited;
            }
            return json;
        },

        // Create a new model with identical attributes to this one.
        clone:function () {
            return new this.constructor(this.toJSON());
        },

        //Navigate the path to the leaf object in the path to query for the attribute value
        getAttr:function (path) {

            var result = this,
            //Tokenize the path
                attrs = getPathArray(path),
                key,
                i;
            if (_.size(attrs) < 1) return;
            for (i = 0; i < attrs.length; i++) {
                key = attrs[i];
                if (!result) break;
                //Navigate the path to get to the result
                result = result instanceof BackboneCollection && (!isNaN(key)) ? result.at(key) : result.attributes[key];
            }
            return result;
        }
    });

    var delimiters = /[^\.\[\]]+/g;

    // Tokenize the fully qualified event path
    var getPathArray = function (path) {
        if (path === '') return [''];
        return _.isString(path) ? (path.match(delimiters)) : path || [];
    };

    //Infer the relation from the collection's parents and find the appropriate map for the passed in `models`
    var map2models = function (parents, target, models) {
        var relation;
        //Iterate over collection's parents
        _.find(parents, function (parent) {
            //Iterate over relations
            relation = _.find(parent.relations, function (rel) {
                return parent.get(rel.key) === target;
            }, this);
            if (relation) return true;//break;
        }, this);

        //If we found a relation and it has a mapping function
        if (relation && relation.map) {
            return relation.map(models)
        }
        return models;
    };

    var proxies = {};
    // Proxy Backbone collection methods
    _.each(['set', 'remove', 'reset'], function (method) {
        proxies[method] = BackboneCollection.prototype[method];

        CollectionProto[method] = function (models, options) {
            //Short-circuit if this collection doesn't hold `AssociatedModels`
            if (this.model.prototype instanceof AssociatedModel && this.parents) {
                //Find a map function if available and perform a transformation
                arguments[0] = map2models(this.parents, this, models);
            }
            return proxies[method].apply(this, arguments);
        }
    });

    // Version
    AssociatedModel.VERSION = "0.4.2";

}).call(this);
<|endoftext|>