<commit_before>import assert from 'assert'
import math from '../../../src/entry/bundleAny'
import approx from '../../../tools/approx'
const pi = math.pi
const complex = math.complex
const matrix = math.matrix
const unit = math.unit
const sin = math.sin
const bigmath = math.create({ precision: 242 })

describe('sin', function () {
  it('should return the sine of a boolean', function () {
    approx.equal(sin(true), 0.841470984807897)
    approx.equal(sin(false), 0)
  })

  it('should return the sine of a number', function () {
    approx.equal(sin(0), 0)
    approx.equal(sin(pi / 8), 0.382683432365090)
    approx.equal(sin(pi / 4), Math.SQRT1_2)
    approx.equal(sin(pi / 2), 1)
    approx.equal(sin(pi * 3 / 4), 0.707106781186548)
    approx.equal(sin(pi), 0)
    approx.equal(sin(pi * 5 / 4), -0.707106781186548)
    approx.equal(sin(pi * 3 / 2), -1)
    approx.equal(sin(pi * 7 / 4), -0.707106781186548)
    approx.equal(sin(pi * 2), 0)
  })

  it('should return the sine of a bignumber', function () {
    const Big = bigmath.bignumber
    assert.deepStrictEqual(bigmath.sin(Big(0)), Big(0))

    // 103.64 % tau = 3.109... <- pretty close to the pi boundary
    let resultVal = bigmath.sin(Big(103.64))
    assert.deepStrictEqual(resultVal, Big('0.0325518169566161584427313159942672130512044591216893328934710' +
                                        '307148043832988055013958395123418887322610809247793661058554' +
                                        '935758353628919004205593985094895305777198408601067175226892' +
                                        '4960612126026291341865833521451170868744460464214033460336158'))

    const arg = Big(-103.64)
    resultVal = bigmath.sin(arg)
    assert.deepStrictEqual(arg, Big(-103.64)) // Make sure arg wasn't changed
    assert.deepStrictEqual(resultVal, Big('-0.0325518169566161584427313159942672130512044591216893328934710' +
                                        '3071480438329880550139583951234188873226108092477936610585549' +
                                        '3575835362891900420559398509489530577719840860106717522689249' +
                                        '60612126026291341865833521451170868744460464214033460336158'))
    const bigmath2 = bigmath.create({ number: 'BigNumber', precision: 15 })

    // we've had a bug in reducing the period, affecting integer values around multiples of tau (like 6, 7)
    for (let x = -20; x < 20; x += 1) {
      approx.equal(bigmath2.sin(bigmath2.bignumber(x)).toNumber(), Math.sin(x))
    }

    const bigPi = bigmath2.pi
    assert.deepStrictEqual(bigmath2.sin(bigPi.div(8)).toString(), '0.38268343236509')
    assert.deepStrictEqual(bigmath2.sin(bigPi.div(4)).toString(), '0.707106781186547')
    assert.deepStrictEqual(bigmath2.sin(bigPi.div(2)).toString(), '1')
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(3).div(4)).toString(), '0.707106781186551')
    assert.ok(bigmath2.sin(bigPi).lt(1e-14))
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(5).div(4)).toString(), '-0.707106781186554')
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(3).div(2)).toString(), '-1')
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(7).div(4)).toString(), '-0.707106781186553')
    assert.ok(bigmath2.sin(bigPi.times(2)).lt(1e-13))
    assert.ok(bigmath2.sin(bigmath2.tau).lt(1e-14))
    assert.ok(bigmath2.sin(bigmath2.tau.times(2)).lt(1e-13))

    const bigmath61 = bigmath.create({ number: 'BigNumber', precision: 61 })

    assert.deepStrictEqual(bigmath61.sin(bigmath61.bignumber(-2)).toString(), '-0.909297426825681695396019865911744842702254971447890268378973')
  })

  it('should return the sine of a complex number', function () {
    const re = 9.15449914691143
    const im = 4.16890695996656
    approx.deepEqual(sin(complex('2+3i')), complex(re, -im))
    approx.deepEqual(sin(complex('2-3i')), complex(re, im))
    approx.deepEqual(sin(complex('-2+3i')), complex(-re, -im))
    approx.deepEqual(sin(complex('-2-3i')), complex(-re, im))
    approx.deepEqual(sin(complex('i')), complex(0, 1.175201193643801))
    approx.deepEqual(sin(complex('1')), complex(0.841470984807897, 0))
    approx.deepEqual(sin(complex('1+i')), complex(1.298457581415977, 0.634963914784736))
    approx.deepEqual(sin(complex('1e-10i')), complex('1e-10i'))
  })

  it('should return the sine of an angle', function () {
    approx.equal(sin(unit('45deg')), 0.707106781186548)
    approx.equal(sin(unit('-45deg')), -0.707106781186548)

    assert(math.isBigNumber(sin(unit(math.bignumber(45), 'deg'))))
    approx.equal(sin(unit(math.bignumber(45), 'deg')).toNumber(), 0.707106781186548)

    approx.deepEqual(sin(unit(complex('1+i'), 'rad')), complex(1.298457581415977, 0.634963914784736))
  })

  it('should throw an error if called with an invalid unit', function () {
    assert.throws(function () { sin(unit('5 celsius')) })
  })

  it('should throw an error if called with a string', function () {
    assert.throws(function () { sin('string') })
  })

  const sin123 = [0.84147098480789, 0.909297426825682, 0.141120008059867]

  it('should return the sin of each element of an array', function () {
    approx.deepEqual(sin([1, 2, 3]), sin123)
  })

  it('should return the sin of each element of a matrix', function () {
    approx.deepEqual(sin(matrix([1, 2, 3])), matrix(sin123))
  })

  it('should throw an error in case of invalid number of arguments', function () {
    assert.throws(function () { sin() }, /TypeError: Too few arguments/)
    assert.throws(function () { sin(1, 2) }, /TypeError: Too many arguments/)
  })

  it('should LaTeX sin', function () {
    const expression = math.parse('sin(0.5)')
    assert.strictEqual(expression.toTex(), '\\sin\\left(0.5\\right)')
  })
})
<commit_msg>Fix lint error<commit_after>import assert from 'assert'
import math from '../../../src/entry/bundleAny'
import approx from '../../../tools/approx'
const pi = math.pi
const complex = math.complex
const matrix = math.matrix
const unit = math.unit
const sin = math.sin
const bigmath = math.create({ precision: 242 })

describe('sin', function () {
  it('should return the sine of a boolean', function () {
    approx.equal(sin(true), 0.841470984807897)
    approx.equal(sin(false), 0)
  })

  it('should return the sine of a number', function () {
    approx.equal(sin(0), 0)
    approx.equal(sin(pi / 8), 0.382683432365090)
    approx.equal(sin(pi / 4), Math.SQRT1_2)
    approx.equal(sin(pi / 2), 1)
    approx.equal(sin(pi * 3 / 4), 0.707106781186548)
    approx.equal(sin(pi), 0)
    approx.equal(sin(pi * 5 / 4), -0.707106781186548)
    approx.equal(sin(pi * 3 / 2), -1)
    approx.equal(sin(pi * 7 / 4), -0.707106781186548)
    approx.equal(sin(pi * 2), 0)
  })

  it('should return the sine of a bignumber', function () {
    const Big = bigmath.bignumber
    assert.deepStrictEqual(bigmath.sin(Big(0)), Big(0))

    // 103.64 % tau = 3.109... <- pretty close to the pi boundary
    let resultVal = bigmath.sin(Big(103.64))
    assert.deepStrictEqual(resultVal, Big('0.0325518169566161584427313159942672130512044591216893328934710' +
                                        '307148043832988055013958395123418887322610809247793661058554' +
                                        '935758353628919004205593985094895305777198408601067175226892' +
                                        '4960612126026291341865833521451170868744460464214033460336158'))

    const arg = Big(-103.64)
    resultVal = bigmath.sin(arg)
    assert.deepStrictEqual(arg, Big(-103.64)) // Make sure arg wasn't changed
    assert.deepStrictEqual(resultVal, Big('-0.0325518169566161584427313159942672130512044591216893328934710' +
                                        '3071480438329880550139583951234188873226108092477936610585549' +
                                        '3575835362891900420559398509489530577719840860106717522689249' +
                                        '60612126026291341865833521451170868744460464214033460336158'))
    const bigmath2 = bigmath.create({ number: 'BigNumber', precision: 15 })

    // we've had a bug in reducing the period, affecting integer values around multiples of tau (like 6, 7)
    for (let x = -20; x < 20; x += 1) {
      approx.equal(bigmath2.sin(bigmath2.bignumber(x)).toNumber(), Math.sin(x))
    }

    const bigPi = bigmath2.pi
    assert.deepStrictEqual(bigmath2.sin(bigPi.div(8)).toString(), '0.38268343236509')
    assert.deepStrictEqual(bigmath2.sin(bigPi.div(4)).toString(), '0.707106781186547')
    assert.deepStrictEqual(bigmath2.sin(bigPi.div(2)).toString(), '1')
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(3).div(4)).toString(), '0.707106781186551')
    assert.ok(bigmath2.sin(bigPi).lt(1e-14))
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(5).div(4)).toString(), '-0.707106781186554')
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(3).div(2)).toString(), '-1')
    assert.deepStrictEqual(bigmath2.sin(bigPi.times(7).div(4)).toString(), '-0.707106781186553')
    assert.ok(bigmath2.sin(bigPi.times(2)).lt(1e-13))
    assert.ok(bigmath2.sin(bigmath2.tau).lt(1e-14))
    assert.ok(bigmath2.sin(bigmath2.tau.times(2)).lt(1e-13))

    const bigmath61 = bigmath.create({ number: 'BigNumber', precision: 61 })

    assert.deepStrictEqual(bigmath61.sin(bigmath61.bignumber(-2)).toString(), '-0.909297426825681695396019865911744842702254971447890268378973')
  })

  it('should return the sine of a complex number', function () {
    const re = 9.15449914691143
    const im = 4.16890695996656
    approx.deepEqual(sin(complex('2+3i')), complex(re, -im))
    approx.deepEqual(sin(complex('2-3i')), complex(re, im))
    approx.deepEqual(sin(complex('-2+3i')), complex(-re, -im))
    approx.deepEqual(sin(complex('-2-3i')), complex(-re, im))
    approx.deepEqual(sin(complex('i')), complex(0, 1.175201193643801))
    approx.deepEqual(sin(complex('1')), complex(0.841470984807897, 0))
    approx.deepEqual(sin(complex('1+i')), complex(1.298457581415977, 0.634963914784736))
    approx.deepEqual(sin(complex('1e-10i')), complex('1e-10i'))
  })

  it('should return the sine of an angle', function () {
    approx.equal(sin(unit('45deg')), 0.707106781186548)
    approx.equal(sin(unit('-45deg')), -0.707106781186548)

    assert(math.isBigNumber(sin(unit(math.bignumber(45), 'deg'))))
    approx.equal(sin(unit(math.bignumber(45), 'deg')).toNumber(), 0.707106781186548)

    approx.deepEqual(sin(unit(complex('1+i'), 'rad')), complex(1.298457581415977, 0.634963914784736))
  })

  it('should throw an error if called with an invalid unit', function () {
    assert.throws(function () { sin(unit('5 celsius')) })
  })

  it('should throw an error if called with a string', function () {
    assert.throws(function () { sin('string') })
  })

  const sin123 = [0.84147098480789, 0.909297426825682, 0.141120008059867]

  it('should return the sin of each element of an array', function () {
    approx.deepEqual(sin([1, 2, 3]), sin123)
  })

  it('should return the sin of each element of a matrix', function () {
    approx.deepEqual(sin(matrix([1, 2, 3])), matrix(sin123))
  })

  it('should throw an error in case of invalid number of arguments', function () {
    assert.throws(function () { sin() }, /TypeError: Too few arguments/)
    assert.throws(function () { sin(1, 2) }, /TypeError: Too many arguments/)
  })

  it('should LaTeX sin', function () {
    const expression = math.parse('sin(0.5)')
    assert.strictEqual(expression.toTex(), '\\sin\\left(0.5\\right)')
  })
})
<|endoftext|>


<commit_before>var _ = require('underscore');
var Model = require('../core/model');
var BackboneCancelSync = require('../util/backbone-abort-sync');
var WindshaftFiltersBoundingBoxFilter = require('../windshaft/filters/bounding-box');
var BOUNDING_BOX_FILTER_WAIT = 500;

var UNFETCHED_STATUS = 'unfeteched';
var FETCHING_STATUS = 'fetching';
var FETCHED_STATUS = 'fetched';
var FETCH_ERROR_STATUS = 'error';

/**
 * Default dataview model
 */
module.exports = Model.extend({
  defaults: {
    url: '',
    data: [],
    sync_on_data_change: true,
    sync_on_bbox_change: true,
    enabled: true,
    status: UNFETCHED_STATUS
  },

  url: function () {
    var params = _.union(
      [ this._getBoundingBoxFilterParam() ],
      this._getDataviewSpecificURLParams()
    );

    if (this.get('apiKey')) {
      params.push('api_key=' + this.get('apiKey'));
    } else if (this.get('authToken')) {
      var authToken = this.get('authToken');
      if (authToken instanceof Array) {
        _.each(authToken, function (token) {
          params.push('auth_token[]=' + token);
        });
      } else {
        params.push('auth_token=' + authToken);
      }
    }
    return this.get('url') + '?' + params.join('&');
  },

  _getBoundingBoxFilterParam: function () {
    var result = '';
    var boundingBoxFilter;

    if (this.syncsOnBoundingBoxChanges()) {
      boundingBoxFilter = new WindshaftFiltersBoundingBoxFilter(this._getMapViewBounds());
      result = 'bbox=' + boundingBoxFilter.toString();
    }

    return result;
  },

  _getMapViewBounds: function () {
    return this._map.getViewBounds();
  },

  /**
   * Subclasses might override this method to define extra params that will be appended
   * to the dataview's URL.
   * @return {[Array]} An array of strings in the form of "key=value".
   */
  _getDataviewSpecificURLParams: function () {
    return [];
  },

  initialize: function (attrs, opts) {
    attrs = attrs || {};
    opts = opts || {};

    if (!opts.map) throw new Error('map is required');
    if (!opts.vis) throw new Error('vis is required');
    if (!opts.analysisCollection) throw new Error('analysisCollection is required');
    if (!attrs.source) throw new Error('source is a required attr');

    if (!attrs.id) {
      this.set('id', this.defaults.type + '-' + this.cid);
    }

    this.layer = opts.layer;
    this._map = opts.map;
    this._vis = opts.vis;
    this._analysisCollection = opts.analysisCollection;

    this.sync = BackboneCancelSync.bind(this);

    // filter is optional, so have to guard before using it
    this.filter = opts.filter;
    if (this.filter) {
      this.filter.set('dataviewId', this.id);
    }

    this._initBinds();
    this._setupAnalysisStatusEvents();
  },

  _getLayerDataProvider: function () {
    return this.layer.getDataProvider();
  },

  _initBinds: function () {
    this.listenTo(this.layer, 'change:visible', this._onLayerVisibilityChanged);
    this.listenTo(this.layer, 'change:source', this._setupAnalysisStatusEvents);
    this.on('change:source', this._setupAnalysisStatusEvents, this);

    var layerDataProvider = this._getLayerDataProvider();
    if (layerDataProvider) {
      this.listenToOnce(layerDataProvider, 'dataChanged', this._onChangeBinds, this);
      this.listenTo(layerDataProvider, 'dataChanged', this.fetch);
    } else {
      this.listenToOnce(this, 'change:url', function () {
        if (this.syncsOnBoundingBoxChanges() && !this._getMapViewBounds()) {
          // wait until map gets bounds from view
          this._map.on('change:view_bounds_ne', function () {
            this._initialFetch();
          }, this);
        } else {
          this._initialFetch();
        }
      });
    }

    if (this.filter) {
      this.listenTo(this.filter, 'change', this._onFilterChanged);
    }
  },

  _initialFetch: function () {
    this.fetch({
      success: this._onChangeBinds.bind(this)
    });
  },

  _setupAnalysisStatusEvents: function () {
    this._removeExistingAnalysisBindings();
    this._analysis = this.getSource();
    if (this._analysis) {
      this._analysis.on('change:status', this._onAnalysisStatusChange, this);
    }
  },

  _removeExistingAnalysisBindings: function () {
    if (!this._analysis) return;
    this._analysis.off('change:status', this._onAnalysisStatusChange, this);
  },

  _onAnalysisStatusChange: function (analysis, status) {
    if (analysis.isLoading()) {
      this._triggerLoading();
    } else if (analysis.isFailed()) {
      this._triggerError(analysis.get('error'));
    }
    // loaded will be triggered through the default behavior, so not necessary to react on that status here
  },

  _triggerLoading: function () {
    this.trigger('loading', this);
  },

  _triggerError: function (error) {
    this.trigger('error', this, error);
  },

  /**
   * @private
   */
  _onFilterChanged: function (filter) {
    var layerDataProvider = this._getLayerDataProvider();
    if (layerDataProvider && layerDataProvider.canApplyFilterTo(this)) {
      layerDataProvider.applyFilter(this, filter);
    } else {
      this._reloadVis();
    }
  },

  /**
   * @protected
   */
  _reloadVis: function (opts) {
    opts = opts || {};
    this._vis.reload(
      _.extend(
        opts, {
          sourceId: this.getSourceId()
        }
      )
    );
  },

  _reloadVisAndForceFetch: function () {
    this._reloadVis({
      forceFetch: true
    });
  },

  /**
   * Enable/disable the dataview depending on the layer visibility.
   * @private
   * @param  {LayerModel} model the layer model which visible property has changed.
   * @param  {Boolean} value New value for visible.
   * @returns {void}
   */
  _onLayerVisibilityChanged: function (model, value) {
    this.set({enabled: value});
  },

  _onChangeBinds: function () {
    this.on('change:sync_on_bbox_change', function () {
      this.refresh();
    }, this);

    this.listenTo(this._map, 'change:center change:zoom', _.debounce(this._onMapBoundsChanged.bind(this), BOUNDING_BOX_FILTER_WAIT));

    this.on('change:url', function (model, value, opts) {
      if (this.syncsOnDataChanges()) {
        this._newDataAvailable = true;
      }
      if (this._shouldFetchOnURLChange(opts && _.pick(opts, ['forceFetch', 'sourceId']))) {
        this.fetch();
      }
    }, this);

    this.on('change:enabled', function (mdl, isEnabled) {
      if (isEnabled && this._newDataAvailable) {
        this.fetch();
        this._newDataAvailable = false;
      }
    }, this);
  },

  _onMapBoundsChanged: function () {
    if (this._shouldFetchOnBoundingBoxChange()) {
      this.fetch();
    }

    if (this.syncsOnBoundingBoxChanges()) {
      this._newDataAvailable = true;
    }
  },

  _shouldFetchOnURLChange: function (options) {
    options = options || {};
    var sourceId = options.sourceId;
    var forceFetch = options.forceFetch;

    if (forceFetch) {
      return true;
    }

    return this.isEnabled() &&
      this.syncsOnDataChanges() &&
        this._sourceAffectsMyOwnSource(sourceId);
  },

  _sourceAffectsMyOwnSource: function (sourceId) {
    if (!sourceId) {
      return true;
    }
    var sourceAnalysis = this.getSource();
    return sourceAnalysis && sourceAnalysis.findAnalysisById(sourceId);
  },

  _shouldFetchOnBoundingBoxChange: function () {
    return this.isEnabled() &&
      this.syncsOnBoundingBoxChanges();
  },

  refresh: function () {
    this.fetch();
  },

  update: function (attrs) {
    attrs = _.pick(attrs, this.constructor.ATTRS_NAMES);
    this.set(attrs);
  },

  getData: function () {
    return this.get('data');
  },

  getPreviousData: function () {
    return this.previous('data');
  },

  fetch: function (opts) {
    opts = opts || {};
    this.set('status', FETCHING_STATUS);
    var layerDataProvider = this._getLayerDataProvider();
    if (layerDataProvider && layerDataProvider.canProvideDataFor(this)) {
      this.set(this.parse(layerDataProvider.getDataFor(this)));
    } else {
      this._triggerLoading();

      if (opts.success) {
        var successCallback = opts && opts.success;
      }

      return Model.prototype.fetch.call(this, _.extend(opts, {
        success: function () {
          this.set('status', FETCHED_STATUS);
          successCallback && successCallback(arguments);
          this.trigger('loaded', this);
        }.bind(this),
        error: function (mdl, err) {
          this.set('status', FETCH_ERROR_STATUS);
          if (!err || (err && err.statusText !== 'abort')) {
            this._triggerError(err);
          }
        }.bind(this)
      }));
    }
  },

  toJSON: function () {
    throw new Error('toJSON should be defined for each dataview');
  },

  getSource: function () {
    var sourceId = this.getSourceId();
    return sourceId && this._analysisCollection.get(sourceId);
  },

  getSourceId: function () {
    // Dataview is pointing to a layer that has a source, so its
    // source is actually the the layers's source
    if (this.hasLayerAsSource() && this.layer.has('source')) {
      return this.layer.get('source');
    }

    // Dataview is pointing to a layer with `sql` or an analysis
    // node directly, so just return the id that has been set by
    // dataviews-factory.js
    return this._ownSourceId();
  },

  _ownSourceId: function () {
    return this.has('source') && this.get('source').id;
  },

  hasLayerAsSource: function () {
    return this._ownSourceId() === this.layer.id;
  },

  isFiltered: function () {
    var isFiltered = false;
    if (this.filter) {
      isFiltered = !this.filter.isEmpty();
    }
    return isFiltered;
  },

  remove: function () {
    this._removeExistingAnalysisBindings();
    this.trigger('destroy', this);
    this.stopListening();
  },

  isFetched: function () {
    return this.get('status') === FETCHED_STATUS;
  },

  isUnavailable: function () {
    return this.get('status') === FETCH_ERROR_STATUS;
  },

  isEnabled: function () {
    return this.get('enabled');
  },

  syncsOnDataChanges: function () {
    return this.get('sync_on_data_change');
  },

  syncsOnBoundingBoxChanges: function () {
    return this.get('sync_on_bbox_change');
  }
},

  // Class props
  {
    ATTRS_NAMES: [
      'id',
      'sync_on_data_change',
      'sync_on_bbox_change',
      'enabled',
      'source'
    ]
  }
);
<commit_msg>.getSourceType<commit_after>var _ = require('underscore');
var Model = require('../core/model');
var BackboneCancelSync = require('../util/backbone-abort-sync');
var WindshaftFiltersBoundingBoxFilter = require('../windshaft/filters/bounding-box');
var BOUNDING_BOX_FILTER_WAIT = 500;

var UNFETCHED_STATUS = 'unfeteched';
var FETCHING_STATUS = 'fetching';
var FETCHED_STATUS = 'fetched';
var FETCH_ERROR_STATUS = 'error';

/**
 * Default dataview model
 */
module.exports = Model.extend({
  defaults: {
    url: '',
    data: [],
    sync_on_data_change: true,
    sync_on_bbox_change: true,
    enabled: true,
    status: UNFETCHED_STATUS
  },

  url: function () {
    var params = _.union(
      [ this._getBoundingBoxFilterParam() ],
      this._getDataviewSpecificURLParams()
    );

    if (this.get('apiKey')) {
      params.push('api_key=' + this.get('apiKey'));
    } else if (this.get('authToken')) {
      var authToken = this.get('authToken');
      if (authToken instanceof Array) {
        _.each(authToken, function (token) {
          params.push('auth_token[]=' + token);
        });
      } else {
        params.push('auth_token=' + authToken);
      }
    }
    return this.get('url') + '?' + params.join('&');
  },

  _getBoundingBoxFilterParam: function () {
    var result = '';
    var boundingBoxFilter;

    if (this.syncsOnBoundingBoxChanges()) {
      boundingBoxFilter = new WindshaftFiltersBoundingBoxFilter(this._getMapViewBounds());
      result = 'bbox=' + boundingBoxFilter.toString();
    }

    return result;
  },

  _getMapViewBounds: function () {
    return this._map.getViewBounds();
  },

  /**
   * Subclasses might override this method to define extra params that will be appended
   * to the dataview's URL.
   * @return {[Array]} An array of strings in the form of "key=value".
   */
  _getDataviewSpecificURLParams: function () {
    return [];
  },

  initialize: function (attrs, opts) {
    attrs = attrs || {};
    opts = opts || {};

    if (!opts.map) throw new Error('map is required');
    if (!opts.vis) throw new Error('vis is required');
    if (!opts.analysisCollection) throw new Error('analysisCollection is required');
    if (!attrs.source) throw new Error('source is a required attr');

    if (!attrs.id) {
      this.set('id', this.defaults.type + '-' + this.cid);
    }

    this.layer = opts.layer;
    this._map = opts.map;
    this._vis = opts.vis;
    this._analysisCollection = opts.analysisCollection;

    this.sync = BackboneCancelSync.bind(this);

    // filter is optional, so have to guard before using it
    this.filter = opts.filter;
    if (this.filter) {
      this.filter.set('dataviewId', this.id);
    }

    this._initBinds();
    this._setupAnalysisStatusEvents();
  },

  _getLayerDataProvider: function () {
    return this.layer.getDataProvider();
  },

  _initBinds: function () {
    this.listenTo(this.layer, 'change:visible', this._onLayerVisibilityChanged);
    this.listenTo(this.layer, 'change:source', this._setupAnalysisStatusEvents);
    this.on('change:source', this._setupAnalysisStatusEvents, this);

    var layerDataProvider = this._getLayerDataProvider();
    if (layerDataProvider) {
      this.listenToOnce(layerDataProvider, 'dataChanged', this._onChangeBinds, this);
      this.listenTo(layerDataProvider, 'dataChanged', this.fetch);
    } else {
      this.listenToOnce(this, 'change:url', function () {
        if (this.syncsOnBoundingBoxChanges() && !this._getMapViewBounds()) {
          // wait until map gets bounds from view
          this._map.on('change:view_bounds_ne', function () {
            this._initialFetch();
          }, this);
        } else {
          this._initialFetch();
        }
      });
    }

    if (this.filter) {
      this.listenTo(this.filter, 'change', this._onFilterChanged);
    }
  },

  _initialFetch: function () {
    this.fetch({
      success: this._onChangeBinds.bind(this)
    });
  },

  _setupAnalysisStatusEvents: function () {
    this._removeExistingAnalysisBindings();
    this._analysis = this.getSource();
    if (this._analysis) {
      this._analysis.on('change:status', this._onAnalysisStatusChange, this);
    }
  },

  _removeExistingAnalysisBindings: function () {
    if (!this._analysis) return;
    this._analysis.off('change:status', this._onAnalysisStatusChange, this);
  },

  _onAnalysisStatusChange: function (analysis, status) {
    if (analysis.isLoading()) {
      this._triggerLoading();
    } else if (analysis.isFailed()) {
      this._triggerError(analysis.get('error'));
    }
    // loaded will be triggered through the default behavior, so not necessary to react on that status here
  },

  _triggerLoading: function () {
    this.trigger('loading', this);
  },

  _triggerError: function (error) {
    this.trigger('error', this, error);
  },

  /**
   * @private
   */
  _onFilterChanged: function (filter) {
    var layerDataProvider = this._getLayerDataProvider();
    if (layerDataProvider && layerDataProvider.canApplyFilterTo(this)) {
      layerDataProvider.applyFilter(this, filter);
    } else {
      this._reloadVis();
    }
  },

  /**
   * @protected
   */
  _reloadVis: function (opts) {
    opts = opts || {};
    this._vis.reload(
      _.extend(
        opts, {
          sourceId: this.getSourceId()
        }
      )
    );
  },

  _reloadVisAndForceFetch: function () {
    this._reloadVis({
      forceFetch: true
    });
  },

  /**
   * Enable/disable the dataview depending on the layer visibility.
   * @private
   * @param  {LayerModel} model the layer model which visible property has changed.
   * @param  {Boolean} value New value for visible.
   * @returns {void}
   */
  _onLayerVisibilityChanged: function (model, value) {
    this.set({enabled: value});
  },

  _onChangeBinds: function () {
    this.on('change:sync_on_bbox_change', function () {
      this.refresh();
    }, this);

    this.listenTo(this._map, 'change:center change:zoom', _.debounce(this._onMapBoundsChanged.bind(this), BOUNDING_BOX_FILTER_WAIT));

    this.on('change:url', function (model, value, opts) {
      if (this.syncsOnDataChanges()) {
        this._newDataAvailable = true;
      }
      if (this._shouldFetchOnURLChange(opts && _.pick(opts, ['forceFetch', 'sourceId']))) {
        this.fetch();
      }
    }, this);

    this.on('change:enabled', function (mdl, isEnabled) {
      if (isEnabled && this._newDataAvailable) {
        this.fetch();
        this._newDataAvailable = false;
      }
    }, this);
  },

  _onMapBoundsChanged: function () {
    if (this._shouldFetchOnBoundingBoxChange()) {
      this.fetch();
    }

    if (this.syncsOnBoundingBoxChanges()) {
      this._newDataAvailable = true;
    }
  },

  _shouldFetchOnURLChange: function (options) {
    options = options || {};
    var sourceId = options.sourceId;
    var forceFetch = options.forceFetch;

    if (forceFetch) {
      return true;
    }

    return this.isEnabled() &&
      this.syncsOnDataChanges() &&
        this._sourceAffectsMyOwnSource(sourceId);
  },

  _sourceAffectsMyOwnSource: function (sourceId) {
    if (!sourceId) {
      return true;
    }
    var sourceAnalysis = this.getSource();
    return sourceAnalysis && sourceAnalysis.findAnalysisById(sourceId);
  },

  _shouldFetchOnBoundingBoxChange: function () {
    return this.isEnabled() &&
      this.syncsOnBoundingBoxChanges();
  },

  refresh: function () {
    this.fetch();
  },

  update: function (attrs) {
    attrs = _.pick(attrs, this.constructor.ATTRS_NAMES);
    this.set(attrs);
  },

  getData: function () {
    return this.get('data');
  },

  getPreviousData: function () {
    return this.previous('data');
  },

  fetch: function (opts) {
    opts = opts || {};
    this.set('status', FETCHING_STATUS);
    var layerDataProvider = this._getLayerDataProvider();
    if (layerDataProvider && layerDataProvider.canProvideDataFor(this)) {
      this.set(this.parse(layerDataProvider.getDataFor(this)));
    } else {
      this._triggerLoading();

      if (opts.success) {
        var successCallback = opts && opts.success;
      }

      return Model.prototype.fetch.call(this, _.extend(opts, {
        success: function () {
          this.set('status', FETCHED_STATUS);
          successCallback && successCallback(arguments);
          this.trigger('loaded', this);
        }.bind(this),
        error: function (mdl, err) {
          this.set('status', FETCH_ERROR_STATUS);
          if (!err || (err && err.statusText !== 'abort')) {
            this._triggerError(err);
          }
        }.bind(this)
      }));
    }
  },

  toJSON: function () {
    throw new Error('toJSON should be defined for each dataview');
  },

  getSourceType: function () {
    var sourceAnalysis = this.getSource();
    return sourceAnalysis.get('type');
  },

  getSource: function () {
    var sourceId = this.getSourceId();
    return sourceId && this._analysisCollection.get(sourceId);
  },

  getSourceId: function () {
    // Dataview is pointing to a layer that has a source, so its
    // source is actually the the layers's source
    if (this.hasLayerAsSource() && this.layer.has('source')) {
      return this.layer.get('source');
    }

    // Dataview is pointing to a layer with `sql` or an analysis
    // node directly, so just return the id that has been set by
    // dataviews-factory.js
    return this._ownSourceId();
  },

  _ownSourceId: function () {
    return this.has('source') && this.get('source').id;
  },

  hasLayerAsSource: function () {
    return this._ownSourceId() === this.layer.id;
  },

  isFiltered: function () {
    var isFiltered = false;
    if (this.filter) {
      isFiltered = !this.filter.isEmpty();
    }
    return isFiltered;
  },

  remove: function () {
    this._removeExistingAnalysisBindings();
    this.trigger('destroy', this);
    this.stopListening();
  },

  isFetched: function () {
    return this.get('status') === FETCHED_STATUS;
  },

  isUnavailable: function () {
    return this.get('status') === FETCH_ERROR_STATUS;
  },

  isEnabled: function () {
    return this.get('enabled');
  },

  syncsOnDataChanges: function () {
    return this.get('sync_on_data_change');
  },

  syncsOnBoundingBoxChanges: function () {
    return this.get('sync_on_bbox_change');
  }
},

  // Class props
  {
    ATTRS_NAMES: [
      'id',
      'sync_on_data_change',
      'sync_on_bbox_change',
      'enabled',
      'source'
    ]
  }
);
<|endoftext|>


<commit_before>var _ = require('underscore');
var Model = require('../core/model');
var WindshaftFiltersBoundingBoxFilter = require('../windshaft/filters/bounding-box');

/**
 * Default dataview model
 */
module.exports = Model.extend({
  defaults: {
    url: '',
    data: [],
    sync_on_data_change: true,
    sync_on_bbox_change: true,
    enabled: true
  },

  url: function () {
    var params = [];
    if (this.get('boundingBox')) {
      params.push('bbox=' + this.get('boundingBox'));
    }
    return this.get('url') + '?' + params.join('&');
  },

  initialize: function (attrs, opts) {
    attrs = attrs || {};
    opts = opts || {};

    if (!opts.map) {
      throw new Error('map is required');
    }
    if (!opts.windshaftMap) {
      throw new Error('windshaftMap is required');
    }

    if (!attrs.id) {
      this.set('id', this.defaults.type + '-' + this.cid);
    }

    this.layer = opts.layer;
    this._map = opts.map;
    this._windshaftMap = opts.windshaftMap;

    // filter is optional, so have to guard before using it
    this.filter = opts.filter;
    if (this.filter) {
      this.filter.set('dataviewId', this.id);
    }

    this._initBinds();
    this._updateBoundingBox();
  },

  _initBinds: function () {
    this.listenTo(this._windshaftMap, 'instanceCreated', this._onNewWindshaftMapInstance);

    this.listenToOnce(this, 'change:url', function () {
      this.fetch({
        success: this._onChangeBinds.bind(this)
      });
    });

    // Retrigger an event when the filter changes
    if (this.filter) {
      this.listenTo(this.filter, 'change', this._onFilterChanged);
    }
  },

  /**
   * @private
   */
  _onFilterChanged: function () {
    this._reloadMap();
  },

  /**
   * @protected
   */
  _reloadMap: function () {
    this._map.reload({
      sourceLayerId: this.layer.get('id')
    });
  },

  _onNewWindshaftMapInstance: function (windshaftMapInstance, sourceLayerId) {
    var url = windshaftMapInstance.getDataviewURL({
      dataviewId: this.get('id'),
      protocol: 'http'
    });

    if (url) {
      var silent = (sourceLayerId && sourceLayerId !== this.layer.get('id'));

      // TODO: Instead of setting the url here, we could invoke fetch directly
      this.set('url', url, { silent: silent });
    }
  },

  _onMapBoundsChanged: function () {
    this._updateBoundingBox();
  },

  _updateBoundingBox: function () {
    var boundingBoxFilter = new WindshaftFiltersBoundingBoxFilter(this._map.getViewBounds());
    this.set('boundingBox', boundingBoxFilter.toString());
  },

  _onChangeBinds: function () {
    var BOUNDING_BOX_FILTER_WAIT = 500;
    this.listenTo(this._map, 'change:center change:zoom', _.debounce(this._onMapBoundsChanged.bind(this), BOUNDING_BOX_FILTER_WAIT));

    this.on('change:url', function () {
      if (this._shouldFetchOnURLChange()) {
        this.fetch();
      }
    }, this);
    this.on('change:boundingBox', function () {
      if (this._shouldFetchOnBoundingBoxChange()) {
        this.fetch();
      }
    }, this);

    this.on('change:enabled', function (mdl, isEnabled) {
      if (isEnabled) {
        if (mdl.changedAttributes(this._previousAttrs)) {
          this.fetch();
        }
      } else {
        this._previousAttrs = {
          url: this.get('url'),
          boundingBox: this.get('boundingBox')
        };
      }
    }, this);
  },

  _shouldFetchOnURLChange: function () {
    return this.get('sync_on_data_change') && this.get('enabled');
  },

  _shouldFetchOnBoundingBoxChange: function () {
    return this.get('enabled') && this.get('sync_on_bbox_change');
  },

  refresh: function () {
    this.fetch();
  },

  update: function (attrs) {
    attrs = _.pick(attrs, this.constructor.ATTRS_NAMES);
    this.set(attrs);
  },

  getData: function () {
    return this.get('data');
  },

  getPreviousData: function () {
    return this.previous('data');
  },

  fetch: function (opts) {
    opts = opts || {};
    this.trigger('loading', this);
    return Model.prototype.fetch.call(this, _.extend(opts, {
      error: function () {
        this.trigger('error');
      }.bind(this)
    }));
  },

  toJSON: function () {
    throw new Error('toJSON should be defined for each dataview');
  },

  remove: function () {
    if (this.filter) {
      this.filter.remove();
    }
    this.trigger('destroy', this);
    this.stopListening();
  }
},

  // Class props
  {
    ATTRS_NAMES: [
      'id',
      'sync_on_data_change',
      'sync_on_bbox_change',
      'enabled'
    ]
  }
);
<commit_msg>Enable/disable dataview depending on the layer visibility<commit_after>var _ = require('underscore');
var Model = require('../core/model');
var WindshaftFiltersBoundingBoxFilter = require('../windshaft/filters/bounding-box');

/**
 * Default dataview model
 */
module.exports = Model.extend({
  defaults: {
    url: '',
    data: [],
    sync_on_data_change: true,
    sync_on_bbox_change: true,
    enabled: true
  },

  url: function () {
    var params = [];
    if (this.get('boundingBox')) {
      params.push('bbox=' + this.get('boundingBox'));
    }
    return this.get('url') + '?' + params.join('&');
  },

  initialize: function (attrs, opts) {
    attrs = attrs || {};
    opts = opts || {};

    if (!opts.map) {
      throw new Error('map is required');
    }
    if (!opts.windshaftMap) {
      throw new Error('windshaftMap is required');
    }

    if (!attrs.id) {
      this.set('id', this.defaults.type + '-' + this.cid);
    }

    this.layer = opts.layer;
    this._map = opts.map;
    this._windshaftMap = opts.windshaftMap;

    // filter is optional, so have to guard before using it
    this.filter = opts.filter;
    if (this.filter) {
      this.filter.set('dataviewId', this.id);
    }

    this._initBinds();
    this._updateBoundingBox();
  },

  _initBinds: function () {
    this.listenTo(this._windshaftMap, 'instanceCreated', this._onNewWindshaftMapInstance);
    this.listenTo(this.layer, 'change:visible', this._onLayerVisibilityChanged);

    this.listenToOnce(this, 'change:url', function () {
      this.fetch({
        success: this._onChangeBinds.bind(this)
      });
    });

    // Retrigger an event when the filter changes
    if (this.filter) {
      this.listenTo(this.filter, 'change', this._onFilterChanged);
    }
  },

  /**
   * @private
   */
  _onFilterChanged: function () {
    this._reloadMap();
  },

  /**
   * @protected
   */
  _reloadMap: function () {
    this._map.reload({
      sourceLayerId: this.layer.get('id')
    });
  },

  _onNewWindshaftMapInstance: function (windshaftMapInstance, sourceLayerId) {
    var url = windshaftMapInstance.getDataviewURL({
      dataviewId: this.get('id'),
      protocol: 'http'
    });

    if (url) {
      var silent = (sourceLayerId && sourceLayerId !== this.layer.get('id'));

      // TODO: Instead of setting the url here, we could invoke fetch directly
      this.set('url', url, { silent: silent });
    }
  },

  /**
   * Enable/disable the dataview depending on the layer visibility.
   * @private
   * @param  {LayerModel} model the layer model which visible property has changed.
   * @param  {Boolean} value New value for visible.
   * @returns {void}
   */
  _onLayerVisibilityChanged: function (model, value) {
    this.set({enabled: value});
  },

  _onMapBoundsChanged: function () {
    this._updateBoundingBox();
  },

  _updateBoundingBox: function () {
    var boundingBoxFilter = new WindshaftFiltersBoundingBoxFilter(this._map.getViewBounds());
    this.set('boundingBox', boundingBoxFilter.toString());
  },

  _onChangeBinds: function () {
    var BOUNDING_BOX_FILTER_WAIT = 500;
    this.listenTo(this._map, 'change:center change:zoom', _.debounce(this._onMapBoundsChanged.bind(this), BOUNDING_BOX_FILTER_WAIT));

    this.on('change:url', function () {
      if (this._shouldFetchOnURLChange()) {
        this.fetch();
      }
    }, this);
    this.on('change:boundingBox', function () {
      if (this._shouldFetchOnBoundingBoxChange()) {
        this.fetch();
      }
    }, this);

    this.on('change:enabled', function (mdl, isEnabled) {
      if (isEnabled) {
        if (mdl.changedAttributes(this._previousAttrs)) {
          this.fetch();
        }
      } else {
        this._previousAttrs = {
          url: this.get('url'),
          boundingBox: this.get('boundingBox')
        };
      }
    }, this);
  },

  _shouldFetchOnURLChange: function () {
    return this.get('sync_on_data_change') && this.get('enabled');
  },

  _shouldFetchOnBoundingBoxChange: function () {
    return this.get('enabled') && this.get('sync_on_bbox_change');
  },

  refresh: function () {
    this.fetch();
  },

  update: function (attrs) {
    attrs = _.pick(attrs, this.constructor.ATTRS_NAMES);
    this.set(attrs);
  },

  getData: function () {
    return this.get('data');
  },

  getPreviousData: function () {
    return this.previous('data');
  },

  fetch: function (opts) {
    opts = opts || {};
    this.trigger('loading', this);
    return Model.prototype.fetch.call(this, _.extend(opts, {
      error: function () {
        this.trigger('error');
      }.bind(this)
    }));
  },

  toJSON: function () {
    throw new Error('toJSON should be defined for each dataview');
  },

  remove: function () {
    if (this.filter) {
      this.filter.remove();
    }
    this.trigger('destroy', this);
    this.stopListening();
  }
},

  // Class props
  {
    ATTRS_NAMES: [
      'id',
      'sync_on_data_change',
      'sync_on_bbox_change',
      'enabled'
    ]
  }
);
<|endoftext|>


<commit_before>/* -*- coding: utf-8 -*-
* ----------------------------------------------------------------------------
* Copyright (c) 2015 - Chris Kirby
*
* Distributed under the terms of the MIT License.
*
* An IPython notebook extension to hide code inputs and prompts for presentation.
* -----------------------------------------------------------------------------
*/

define([
'jquery',
'notebook/js/celltoolbar',
'notebook/js/keyboard_manager'
],
function ($, celltoolbar, keyboard_manager){
	"use strict";

	var ctb = celltoolbar.CellToolbar;

	function hideCodeSetter(cell, value){
		if (cell.metadata.hideCode == undefined){ cell.metadata.hideCode = false }
        cell.metadata.hideCode = value;
   		toggleHideCode(cell);
	}

	function hideCodeGetter(cell){
		var isHidden = cell.metadata.hideCode;
		toggleHideCode(cell);
		return (isHidden == undefined) ? undefined : isHidden
	}

	function hidePromptSetter(cell, value){
		if (cell.metadata.hidePrompt == undefined){ cell.metadata.hidePrompt = false }
        cell.metadata.hidePrompt = value;
   		toggleHidePrompt(cell);
	}

	function hidePromptGetter(cell){
		var isHidden = cell.metadata.hidePrompt;
		toggleHidePrompt(cell);
		return (isHidden == undefined) ? undefined : isHidden
	}

	function hideOutputSetter(cell, value){
		if (cell.metadata.hideOutput == undefined){ cell.metadata.hideOutput = false }
        cell.metadata.hideOutput = value;
   		toggleHideOutput(cell);
	}

	function hideOutputGetter(cell){
		var isHidden = cell.metadata.hideOutput;
		toggleHideOutput(cell);
		return (isHidden == undefined) ? undefined : isHidden
	}

	function toggleHideCode(cell){
		var c = $(cell.element);
		if (cell.metadata.hideCode && cell.class_config.classname != 'MarkdownCell'){
			c.find('.input_area').hide();
		} else if(cell.class_config.classname != 'MarkdownCell') {
			c.find('.input_area').show();
		}
	}

	function toggleHidePrompt(cell){
		var c = $(cell.element);
		if (cell.metadata.hidePrompt && cell.class_config.classname != 'MarkdownCell'){
			c.find('.prompt').css('visibility','hidden');
		} else if(cell.class_config.classname != 'MarkdownCell') {
			c.find('.prompt').css('visibility','visible');
		}
	}

	function toggleHideOutput(cell){
		var c = $(cell.element);
		if (cell.metadata.hideOutput && cell.class_config.classname != 'MarkdownCell'){
			c.find('.output_wrapper .output_subarea').css('display','none');
		} else if(cell.class_config.classname != 'MarkdownCell') {
			c.find('.output_wrapper .output_subarea').css('display','block');
		}
	}

	/**
	* Add a toolbar button to toggle visibility of all code cells, input/output prompts, and remove any highlighting for the selected cell.
	**/
	function addHideCodeButtonToToolbar(){
		IPython.toolbar.add_buttons_group([
		    {
		     'label' : 'Hide/show code',
		     'icon' : 'fa-code',
		     'callback' : function() { // toggling visibility is adding display: block to the element. Causing celltoolbar = None not work.
		        $('.input').toggle();
		        $('.prompt').toggle();
		        var ctb = $('.ctb_hideshow');
		        if(ctb.hasClass('ctb_show')) {
		        	ctb.removeClass('ctb_show');
		        } else {
		        	ctb.addClass('ctb_show');
		        }

		        var ctb = $('.celltoolbar');
		        if(ctb.hasClass('invisible')){
		        	console.log('visible');
		        	ctb.removeClass('invisible');
		        } else {
		        	console.log('invisible');
		        	ctb.addClass('invisible');
		        }
		        // $('.celltoolbar').toggle();
		        $('.selected').removeClass('selected').addClass('unselected');
		      }
		    },
		    {
		    	'label' : 'Export to HTML',
		    	'icon' : 'fa-file-text-o',
		    	'callback' : function (){
		    		window.location = window.location.origin + window.location.pathname + "/export/html";
		    		Jupyter.notebook.kernel.reconnect();
		    	}
		    },
		    {
		    	'label' : 'Export to PDF via HTML',
		    	'icon' : 'fa-file-pdf-o',
		    	'callback' : function (){
		    		window.location = window.location.origin + window.location.pathname + "/export/pdf";
		    		Jupyter.notebook.kernel.reconnect();
		    	}
		    },
		    {
		    	'label' : 'Export to PDF via Latex',
		    	'icon' : 'fa-file-o',
		    	'callback' : function (){
		    		window.location = window.location.origin + window.location.pathname + "/export/latexpdf";
		    		Jupyter.notebook.kernel.reconnect();
		    	}
		    }
		  ]);
	}

	var hideCodeCallback = ctb.utils.checkbox_ui_generator(	'Hide Code ', hideCodeSetter, hideCodeGetter);

	var hidePromptCallback = ctb.utils.checkbox_ui_generator('Hide Prompts ', hidePromptSetter,	hidePromptGetter);

	var hideOutputCallback = ctb.utils.checkbox_ui_generator('Hide Outputs ', hideOutputSetter,	hideOutputGetter);

	function setup(){
		ctb.register_callback('hide_code.hideCode', hideCodeCallback);
        ctb.register_callback('hide_code.hidePrompts', hidePromptCallback);
        ctb.register_callback('hide_code.hideOutputs', hideOutputCallback);
        ctb.register_preset('Hide code',['hide_code.hidePrompts','hide_code.hideCode','hide_code.hideOutputs']);
        addHideCodeButtonToToolbar();
        // Add keyboard shortcuts
        keyboard_manager.command_shortcuts.add_shortcut('ctrl-q', toggleHideCode);
        keyboard_manager.command_shortcuts.add_shortcut('ctrl-w', toggleHidePrompt);
        keyboard_manager.command_shortcuts.add_shortcut('ctrl-e', toggleHideOutput);

        $.each(Jupyter.notebook.get_cells(), function(index, cell){
        	toggleHidePrompt(cell);
        	toggleHideCode(cell);
        	toggleHideOutput(cell);
        });
	}

	setup();
	console.log('hide_code setup complete');
});
<commit_msg>Finished keyboard shortcuts. Resolves # 32.<commit_after>/* -*- coding: utf-8 -*-
* ----------------------------------------------------------------------------
* Copyright (c) 2015 - Chris Kirby
*
* Distributed under the terms of the MIT License.
*
* An IPython notebook extension to hide code inputs and prompts for presentation.
* -----------------------------------------------------------------------------
*/

define([
'jquery',
'notebook/js/celltoolbar',
'base/js/namespace'
],
function ($, celltoolbar, Jupyter){
	"use strict";

	var ctb = celltoolbar.CellToolbar;

	function hideCodeSetter(cell, value){
		if (cell.metadata.hideCode == undefined){ cell.metadata.hideCode = false }
        cell.metadata.hideCode = value;
   		toggleHideCode(cell);
	}

	function hideCodeGetter(cell){
		var isHidden = cell.metadata.hideCode;
		toggleHideCode(cell);
		return (isHidden == undefined) ? undefined : isHidden
	}

	/*
	* Sets notebook cell's hide code metadata.
	* @param {Notebook Cell} cell
	* @param {Boolean} value
	*/
	function hidePromptSetter(cell, value){
		if (cell.metadata.hidePrompt == undefined){ cell.metadata.hidePrompt = false }
        cell.metadata.hidePrompt = value;
   		toggleHidePrompt(cell);
	}

	function hidePromptGetter(cell){
		var isHidden = cell.metadata.hidePrompt;
		toggleHidePrompt(cell);
		return (isHidden == undefined) ? undefined : isHidden
	}

	function hideOutputSetter(cell, value){
		if (cell.metadata.hideOutput == undefined){ cell.metadata.hideOutput = false }
        cell.metadata.hideOutput = value;
   		toggleHideOutput(cell);
	}

	function hideOutputGetter(cell){
		var isHidden = cell.metadata.hideOutput;
		toggleHideOutput(cell);
		return (isHidden == undefined) ? undefined : isHidden
	}

	function toggleHideCode(cell){
		var c = $(cell.element);
		if (cell.metadata.hideCode && cell.class_config.classname != 'MarkdownCell'){
			c.find('.input_area').hide();
		} else if(cell.class_config.classname != 'MarkdownCell') {
			c.find('.input_area').show();
		}
	}

	function toggleHidePrompt(cell){
		var c = $(cell.element);
		if (cell.metadata.hidePrompt && cell.class_config.classname != 'MarkdownCell'){
			c.find('.prompt').css('visibility','hidden');
		} else if(cell.class_config.classname != 'MarkdownCell') {
			c.find('.prompt').css('visibility','visible');
		}
	}

	function toggleHideOutput(cell){
		var c = $(cell.element);
		if (cell.metadata.hideOutput && cell.class_config.classname != 'MarkdownCell'){
			c.find('.output_wrapper .output_subarea').css('display','none');
		} else if(cell.class_config.classname != 'MarkdownCell') {
			c.find('.output_wrapper .output_subarea').css('display','block');
		}
	}

	/**
	* Add a toolbar button to toggle visibility of all code cells, input/output prompts, and remove any highlighting for the selected cell.
	**/
	function addHideCodeButtonToToolbar(){
		IPython.toolbar.add_buttons_group([
		    {
		     'label' : 'Hide/show code',
		     'icon' : 'fa-code',
		     'callback' : function() { // toggling visibility is adding display: block to the element. Causing celltoolbar = None not work.
		        $('.input').toggle();
		        $('.prompt').toggle();
		        var ctb = $('.ctb_hideshow');
		        if(ctb.hasClass('ctb_show')) {
		        	ctb.removeClass('ctb_show');
		        } else {
		        	ctb.addClass('ctb_show');
		        }

		        var ctb = $('.celltoolbar');
		        if(ctb.hasClass('invisible')){
		        	console.log('visible');
		        	ctb.removeClass('invisible');
		        } else {
		        	console.log('invisible');
		        	ctb.addClass('invisible');
		        }
		        // $('.celltoolbar').toggle();
		        $('.selected').removeClass('selected').addClass('unselected');
		      }
		    },
		    {
		    	'label' : 'Export to HTML',
		    	'icon' : 'fa-file-text-o',
		    	'callback' : function (){
		    		window.location = window.location.origin + window.location.pathname + "/export/html";
		    		Jupyter.notebook.kernel.reconnect();
		    	}
		    },
		    {
		    	'label' : 'Export to PDF via HTML',
		    	'icon' : 'fa-file-pdf-o',
		    	'callback' : function (){
		    		window.location = window.location.origin + window.location.pathname + "/export/pdf";
		    		Jupyter.notebook.kernel.reconnect();
		    	}
		    },
		    {
		    	'label' : 'Export to PDF via Latex',
		    	'icon' : 'fa-file-o',
		    	'callback' : function (){
		    		window.location = window.location.origin + window.location.pathname + "/export/latexpdf";
		    		Jupyter.notebook.kernel.reconnect();
		    	}
		    }
		  ]);
	}

	var hideCodeCallback = ctb.utils.checkbox_ui_generator(	'Hide Code ', hideCodeSetter, hideCodeGetter);

	var hidePromptCallback = ctb.utils.checkbox_ui_generator('Hide Prompts ', hidePromptSetter,	hidePromptGetter);

	var hideOutputCallback = ctb.utils.checkbox_ui_generator('Hide Outputs ', hideOutputSetter,	hideOutputGetter);

	var hideCodeKeyboardAction = Jupyter.keyboard_manager.actions.register({
    	help: 'Hides selected cell\'s code',
    	icon: 'fa-code',
    	help_index: '',
    	handler: function(env){
    		var cells = env.notebook.get_selected_cells();
    		$.each(cells, function(index, value){
    			hideCodeSetter(cells[index], true);
    			try{
    				$($(Jupyter.notebook.get_selected_cell().celltoolbar.inner_element[0]).find('input')[1]).prop('checked', true);
    			} catch(err){
    				//do nothing
    			}
    		});
    	},
    }, 'hide_code_action','hide_code');

	/**
	* Keyboard short cut action to hide selected cells.
	* Binds to W
	**/
	var showCodeKeyboardAction = Jupyter.keyboard_manager.actions.register({
    	help: 'Shows selected cell\'s code',
    	icon: 'fa-code',
    	help_index: '',
    	handler: function(env){
    		var cells = env.notebook.get_selected_cells();
    		$.each(cells, function(index, value){
    			hideCodeSetter(cells[index], false);
    			try{
    				$($(Jupyter.notebook.get_selected_cell().celltoolbar.inner_element[0]).find('input')[1]).prop('checked', false);
    			} catch(err){
    				//do nothing
    			}
    		});
    	},
    }, 'show_code_action','hide_code');

    var hidePromptKeyboardAction = Jupyter.keyboard_manager.actions.register({
    	help: 'Hides selected cell\'s prompts.',
    	icon: 'fa-code',
    	help_index: '',
    	handler: function(env){
    		var cells = env.notebook.get_selected_cells();
    		$.each(cells, function(index, value){
    			hidePromptSetter(cells[index], true);try{
    				$($(Jupyter.notebook.get_selected_cell().celltoolbar.inner_element[0]).find('input')[0]).prop('checked', true);
    			} catch(err){
    				//do nothing
    			}
    		});
    	},
    }, 'hide_prompt_action','hide_code');

	/**
	* Keyboard short cut action to hide selected cells.
	* Binds to W,W
	**/
	var showPromptKeyboardAction = Jupyter.keyboard_manager.actions.register({
    	help: 'Shows selected cell\'s prompts.',
    	icon: 'fa-code',
    	help_index: '',
    	handler: function(env){
    		var cells = env.notebook.get_selected_cells();
    		$.each(cells, function(index, value){
    			hidePromptSetter(cells[index], false);
    			try{
    				$($(Jupyter.notebook.get_selected_cell().celltoolbar.inner_element[0]).find('input')[0]).prop('checked', false)
    			} catch(err){
    				//do nothing
    			};
    		});
    	},
    }, 'show_prompt_action','hide_code');

    var hideOutputKeyboardAction = Jupyter.keyboard_manager.actions.register({
    	help: 'Hides selected cell\'s prompts.',
    	icon: 'fa-code',
    	help_index: '',
    	handler: function(env){
    		var cells = env.notebook.get_selected_cells();
    		$.each(cells, function(index, value){
    			hideOutputSetter(cells[index], true);
    			try{
    				$($(Jupyter.notebook.get_selected_cell().celltoolbar.inner_element[0]).find('input')[2]).prop('checked', true);
    			} catch(err){
    				//do nothing
    			}
    		});
    	},
    }, 'hide_output_action','hide_code');

	/**
	* Keyboard short cut action to hide selected cells.
	* Binds to W,W
	**/
	var showOutputKeyboardAction = Jupyter.keyboard_manager.actions.register({
    	help: 'Shows selected cell\'s prompts.',
    	icon: 'fa-code',
    	help_index: '',
    	handler: function(env){
    		var cells = env.notebook.get_selected_cells();
    		$.each(cells, function(index, value){
    			hideOutputSetter(cells[index], false);
    			try{
    				$($(Jupyter.notebook.get_selected_cell().celltoolbar.inner_element[0]).find('input')[2]).prop('checked', false);
    			} catch(err){
    				//do nothing
    			}
    		});
    	},
    }, 'show_output_action','hide_code');

    function addKeyboardShortcutBindings(){
    	Jupyter.keyboard_manager.command_shortcuts.add_shortcut('e', 'hide_code:hide_code_action', 'hide_code');
        Jupyter.keyboard_manager.command_shortcuts.add_shortcut('shift-e', 'hide_code:show_code_action', 'hide_code');
        Jupyter.keyboard_manager.command_shortcuts.add_shortcut('w', 'hide_code:hide_prompt_action', 'hide_code');
        Jupyter.keyboard_manager.command_shortcuts.add_shortcut('shift-w', 'hide_code:show_prompt_action', 'hide_code');
        Jupyter.keyboard_manager.command_shortcuts.add_shortcut('r', 'hide_code:hide_output_action', 'hide_code');
        Jupyter.keyboard_manager.command_shortcuts.add_shortcut('shift-r', 'hide_code:show_output_action', 'hide_code');
    }

	function setup(){
		ctb.register_callback('hide_code.hideCode', hideCodeCallback);
        ctb.register_callback('hide_code.hidePrompts', hidePromptCallback);
        ctb.register_callback('hide_code.hideOutputs', hideOutputCallback);
        ctb.register_preset('Hide code',['hide_code.hidePrompts','hide_code.hideCode','hide_code.hideOutputs']);
        addHideCodeButtonToToolbar();
        addKeyboardShortcutBindings();

        $.each(Jupyter.notebook.get_cells(), function(index, cell){
        	toggleHidePrompt(cell);
        	toggleHideCode(cell);
        	toggleHideOutput(cell);
        });
	}

	setup();
	console.log('hide_code setup complete');
});
<|endoftext|>


<commit_before>const test = require('tap').test;
const Worker = require('tiny-worker');

const dispatch = require('../../src/dispatch/central-dispatch');
const VirtualMachine = require('../../src/virtual-machine');

// By default Central Dispatch works with the Worker class built into the browser. Tell it to use TinyWorker instead.
dispatch.workerClass = Worker;

class TestInternalExtension {
    constructor () {
        this.status = {};
        this.status.constructorCalled = true;
    }

    getInfo () {
        this.status.getInfoCalled = true;
        return {
            id: 'test-internal-extension',
            name: 'Test Internal Extension',
            blocks: [
                {
                    opcode: 'go'
                }
            ]
        };
    }

    go () {
        this.status.goCalled = true;
    }
}

test('internal extension', t => {
    const vm = new VirtualMachine();

    const extension = new TestInternalExtension();
    t.ok(extension.status.constructorCalled);

    t.notOk(extension.status.getInfoCalled);
    return vm.extensionManager._registerInternalExtension(extension).then(() => {
        t.ok(extension.status.getInfoCalled);

        const func = vm.runtime.getOpcodeFunction('test-internal-extension.go');
        t.type(func, 'function');

        t.notOk(extension.status.goCalled);
        func();
        t.ok(extension.status.goCalled);
    });
});
<commit_msg>Add two menu types to the internal-extension test.<commit_after>const test = require('tap').test;
const Worker = require('tiny-worker');

const dispatch = require('../../src/dispatch/central-dispatch');
const VirtualMachine = require('../../src/virtual-machine');

// By default Central Dispatch works with the Worker class built into the browser. Tell it to use TinyWorker instead.
dispatch.workerClass = Worker;

class TestInternalExtension {
    constructor () {
        this.status = {};
        this.status.constructorCalled = true;
    }

    getInfo () {
        this.status.getInfoCalled = true;
        return {
            id: 'test-internal-extension',
            name: 'Test Internal Extension',
            blocks: [
                {
                    opcode: 'go'
                }
            ],
            menus: {
                simpleMenu: this._buildAMenu(),
                dynamicMenu: '_buildDynamicMenu',
            }
        };
    }

    go () {
        this.status.goCalled = true;
    }

    _buildAMenu() {
        this.status.buildMenuCalled = true;
        return ['abcd', 'efgh', 'ijkl'];
    }

    _buildDynamicMenu() {
        this.status.buildDynamicMenuCalled = true;
        return [1, 2, 3, 4, 6];
    }
}

test('internal extension', t => {
    const vm = new VirtualMachine();

    const extension = new TestInternalExtension();
    t.ok(extension.status.constructorCalled);

    t.notOk(extension.status.getInfoCalled);
    return vm.extensionManager._registerInternalExtension(extension).then(() => {
        t.ok(extension.status.getInfoCalled);

        const func = vm.runtime.getOpcodeFunction('test-internal-extension.go');
        t.type(func, 'function');

        t.notOk(extension.status.goCalled);
        func();
        t.ok(extension.status.goCalled);

        // There should be 2 menus - one is an array, one is the function to call.
        t.equal(vm.runtime._blockInfo[0].menus.length, 2);
        // First menu has 3 items.
        t.equal(
            vm.runtime._blockInfo[0].menus[0].json.args0[0].options.length, 3);
        // Second menu is a dynamic menu and therefore should be a function.
        t.type(
            vm.runtime._blockInfo[0].menus[1].json.args0[0].options, 'function');
    });
});
<|endoftext|>


<commit_before>/*!
 * # Semantic UI - Accordion
 * http://github.com/semantic-org/semantic-ui/
 *
 *
 * Copyright 2015 Contributors
 * Released under the MIT license
 * http://opensource.org/licenses/MIT
 *
 */

;(function ($, window, document, undefined) {

"use strict";

$.fn.accordion = function(parameters) {
  var
    $allModules     = $(this),

    time            = new Date().getTime(),
    performance     = [],

    query           = arguments[0],
    methodInvoked   = (typeof query == 'string'),
    queryArguments  = [].slice.call(arguments, 1),

    requestAnimationFrame = window.requestAnimationFrame
      || window.mozRequestAnimationFrame
      || window.webkitRequestAnimationFrame
      || window.msRequestAnimationFrame
      || function(callback) { setTimeout(callback, 0); },

    returnedValue
  ;
  $allModules
    .each(function() {
      var
        settings        = ( $.isPlainObject(parameters) )
          ? $.extend(true, {}, $.fn.accordion.settings, parameters)
          : $.extend({}, $.fn.accordion.settings),

        className       = settings.className,
        namespace       = settings.namespace,
        selector        = settings.selector,
        error           = settings.error,

        eventNamespace  = '.' + namespace,
        moduleNamespace = 'module-' + namespace,
        moduleSelector  = $allModules.selector || '',

        $module  = $(this),
        $title   = $module.find(selector.title),
        $content = $module.find(selector.content),

        element  = this,
        instance = $module.data(moduleNamespace),
        observer,
        module
      ;

      module = {

        initialize: function() {
          module.debug('Initializing', $module);
          module.bind.events();
          module.observeChanges();
          module.instantiate();
        },

        instantiate: function() {
          instance = module;
          $module
            .data(moduleNamespace, module)
          ;
        },

        destroy: function() {
          module.debug('Destroying previous instance', $module);
          $module
            .off(eventNamespace)
            .removeData(moduleNamespace)
          ;
        },

        refresh: function() {
          $title   = $module.find(selector.title);
          $content = $module.find(selector.content);
        },

        observeChanges: function() {
          if('MutationObserver' in window) {
            observer = new MutationObserver(function(mutations) {
              module.debug('DOM tree modified, updating selector cache');
              module.refresh();
            });
            observer.observe(element, {
              childList : true,
              subtree   : true
            });
            module.debug('Setting up mutation observer', observer);
          }
        },

        bind: {
          events: function() {
            module.debug('Binding delegated events');
            $module
              .on(settings.on + eventNamespace, selector.trigger, module.event.click)
            ;
          }
        },

        event: {
          click: function() {
            module.toggle.call(this);
          }
        },

        toggle: function(query) {
          var
            $activeTitle = (query !== undefined)
              ? (typeof query === 'number')
                ? $title.eq(query)
                : $(query).closest(selector.title)
              : $(this).closest(selector.title),
            $activeContent = $activeTitle.next($content),
            isAnimating = $activeContent.hasClass(className.animating),
            isActive    = $activeContent.hasClass(className.active),
            isOpen      = (isActive && !isAnimating),
            isOpening   = (!isActive && isAnimating)
          ;
          module.debug('Toggling visibility of content', $activeTitle);
          if(isOpen || isOpening) {
            if(settings.collapsible) {
              module.close.call($activeTitle);
            }
            else {
              module.debug('Cannot close accordion content collapsing is disabled');
            }
          }
          else {
            module.open.call($activeTitle);
          }
        },

        open: function(query) {
          var
            $activeTitle = (query !== undefined)
              ? (typeof query === 'number')
                ? $title.eq(query)
                : $(query).closest(selector.title)
              : $(this).closest(selector.title),
            $activeContent = $activeTitle.next($content),
            isAnimating = $activeContent.hasClass(className.animating),
            isActive    = $activeContent.hasClass(className.active),
            isOpen      = (isActive || isAnimating)
          ;
          if(isOpen) {
            module.debug('Accordion already open, skipping', $activeContent);
            return;
          }
          module.debug('Opening accordion content', $activeTitle);
          settings.onOpening.call($activeContent);
          if(settings.exclusive) {
            module.closeOthers.call($activeTitle);
          }
          $activeTitle
            .addClass(className.active)
          ;
          $activeContent
            .stop(true, true)
            .addClass(className.animating)
          ;
          if(settings.animateChildren) {
            if($.fn.transition !== undefined && $module.transition('is supported')) {
              $activeContent
                .children()
                  .transition({
                    animation   : 'fade in',
                    queue       : false,
                    useFailSafe : true,
                    debug       : settings.debug,
                    verbose     : settings.verbose,
                    duration    : settings.duration
                  })
              ;
            }
            else {
              $activeContent
                .children()
                  .stop(true, true)
                  .animate({
                    opacity: 1
                  }, settings.duration, module.resetOpacity)
              ;
            }
          }
          $activeContent
            .slideDown(settings.duration, settings.easing, function() {
              $activeContent
                .removeClass(className.animating)
                .addClass(className.active)
              ;
              module.reset.display.call(this);
              settings.onOpen.call(this);
              settings.onChange.call(this);
            })
          ;
        },

        close: function(query) {
          var
            $activeTitle = (query !== undefined)
              ? (typeof query === 'number')
                ? $title.eq(query)
                : $(query).closest(selector.title)
              : $(this).closest(selector.title),
            $activeContent = $activeTitle.next($content),
            isAnimating    = $activeContent.hasClass(className.animating),
            isActive       = $activeContent.hasClass(className.active),
            isOpening      = (!isActive && isAnimating),
            isClosing      = (isActive && isAnimating)
          ;
          if((isActive || isOpening) && !isClosing) {
            module.debug('Closing accordion content', $activeContent);
            settings.onClosing.call($activeContent);
            $activeTitle
              .removeClass(className.active)
            ;
            $activeContent
              .stop(true, true)
              .addClass(className.animating)
            ;
            if(settings.animateChildren) {
              if($.fn.transition !== undefined && $module.transition('is supported')) {
                $activeContent
                  .children()
                    .transition({
                      animation   : 'fade out',
                      queue       : false,
                      useFailSafe : true,
                      debug       : settings.debug,
                      verbose     : settings.verbose,
                      duration    : settings.duration
                    })
                ;
              }
              else {
                $activeContent
                  .children()
                    .stop(true, true)
                    .animate({
                      opacity: 0
                    }, settings.duration, module.resetOpacity)
                ;
              }
            }
            $activeContent
              .slideUp(settings.duration, settings.easing, function() {
                $activeContent
                  .removeClass(className.animating)
                  .removeClass(className.active)
                ;
                module.reset.display.call(this);
                settings.onClose.call(this);
                settings.onChange.call(this);
              })
            ;
          }
        },

        closeOthers: function(index) {
          var
            $activeTitle = (index !== undefined)
              ? $title.eq(index)
              : $(this).closest(selector.title),
            $parentTitles    = $activeTitle.parents(selector.content).prev(selector.title),
            $activeAccordion = $activeTitle.closest(selector.accordion),
            activeSelector   = selector.title + '.' + className.active + ':visible',
            activeContent    = selector.content + '.' + className.active + ':visible',
            $openTitles,
            $nestedTitles,
            $openContents
          ;
          if(settings.closeNested) {
            $openTitles   = $activeAccordion.find(activeSelector).not($parentTitles);
            $openContents = $openTitles.next($content);
          }
          else {
            $openTitles   = $activeAccordion.find(activeSelector).not($parentTitles);
            $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles);
            $openTitles   = $openTitles.not($nestedTitles);
            $openContents = $openTitles.next($content);
          }
          if( ($openTitles.length > 0) ) {
            module.debug('Exclusive enabled, closing other content', $openTitles);
            $openTitles
              .removeClass(className.active)
            ;
            $openContents
              .removeClass(className.animating)
              .stop(true, true)
            ;
            if(settings.animateChildren) {
              if($.fn.transition !== undefined && $module.transition('is supported')) {
                $openContents
                  .children()
                    .transition({
                      animation   : 'fade out',
                      useFailSafe : true,
                      debug       : settings.debug,
                      verbose     : settings.verbose,
                      duration    : settings.duration
                    })
                ;
              }
              else {
                $openContents
                  .children()
                    .stop(true, true)
                    .animate({
                      opacity: 0
                    }, settings.duration, module.resetOpacity)
                ;
              }
            }
            $openContents
              .slideUp(settings.duration , settings.easing, function() {
                $(this).removeClass(className.active);
                module.reset.display.call(this);
              })
            ;
          }
        },

        reset: {

          display: function() {
            module.verbose('Removing inline display from element', this);
            $(this).css('display', '');
            if( $(this).attr('style') === '') {
              $(this)
                .attr('style', '')
                .removeAttr('style')
              ;
            }
          },

          opacity: function() {
            module.verbose('Removing inline opacity from element', this);
            $(this).css('opacity', '');
            if( $(this).attr('style') === '') {
              $(this)
                .attr('style', '')
                .removeAttr('style')
              ;
            }
          },

        },

        setting: function(name, value) {
          module.debug('Changing setting', name, value);
          if( $.isPlainObject(name) ) {
            $.extend(true, settings, name);
          }
          else if(value !== undefined) {
            settings[name] = value;
          }
          else {
            return settings[name];
          }
        },
        internal: function(name, value) {
          module.debug('Changing internal', name, value);
          if(value !== undefined) {
            if( $.isPlainObject(name) ) {
              $.extend(true, module, name);
            }
            else {
              module[name] = value;
            }
          }
          else {
            return module[name];
          }
        },
        debug: function() {
          if(settings.debug) {
            if(settings.performance) {
              module.performance.log(arguments);
            }
            else {
              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
              module.debug.apply(console, arguments);
            }
          }
        },
        verbose: function() {
          if(settings.verbose && settings.debug) {
            if(settings.performance) {
              module.performance.log(arguments);
            }
            else {
              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
              module.verbose.apply(console, arguments);
            }
          }
        },
        error: function() {
          module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
          module.error.apply(console, arguments);
        },
        performance: {
          log: function(message) {
            var
              currentTime,
              executionTime,
              previousTime
            ;
            if(settings.performance) {
              currentTime   = new Date().getTime();
              previousTime  = time || currentTime;
              executionTime = currentTime - previousTime;
              time          = currentTime;
              performance.push({
                'Name'           : message[0],
                'Arguments'      : [].slice.call(message, 1) || '',
                'Element'        : element,
                'Execution Time' : executionTime
              });
            }
            clearTimeout(module.performance.timer);
            module.performance.timer = setTimeout(module.performance.display, 500);
          },
          display: function() {
            var
              title = settings.name + ':',
              totalTime = 0
            ;
            time = false;
            clearTimeout(module.performance.timer);
            $.each(performance, function(index, data) {
              totalTime += data['Execution Time'];
            });
            title += ' ' + totalTime + 'ms';
            if(moduleSelector) {
              title += ' \'' + moduleSelector + '\'';
            }
            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
              console.groupCollapsed(title);
              if(console.table) {
                console.table(performance);
              }
              else {
                $.each(performance, function(index, data) {
                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
                });
              }
              console.groupEnd();
            }
            performance = [];
          }
        },
        invoke: function(query, passedArguments, context) {
          var
            object = instance,
            maxDepth,
            found,
            response
          ;
          passedArguments = passedArguments || queryArguments;
          context         = element         || context;
          if(typeof query == 'string' && object !== undefined) {
            query    = query.split(/[\. ]/);
            maxDepth = query.length - 1;
            $.each(query, function(depth, value) {
              var camelCaseValue = (depth != maxDepth)
                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
                : query
              ;
              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
                object = object[camelCaseValue];
              }
              else if( object[camelCaseValue] !== undefined ) {
                found = object[camelCaseValue];
                return false;
              }
              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
                object = object[value];
              }
              else if( object[value] !== undefined ) {
                found = object[value];
                return false;
              }
              else {
                module.error(error.method, query);
                return false;
              }
            });
          }
          if ( $.isFunction( found ) ) {
            response = found.apply(context, passedArguments);
          }
          else if(found !== undefined) {
            response = found;
          }
          if($.isArray(returnedValue)) {
            returnedValue.push(response);
          }
          else if(returnedValue !== undefined) {
            returnedValue = [returnedValue, response];
          }
          else if(response !== undefined) {
            returnedValue = response;
          }
          return found;
        }
      };
      if(methodInvoked) {
        if(instance === undefined) {
          module.initialize();
        }
        module.invoke(query);
      }
      else {
        if(instance !== undefined) {
          instance.invoke('destroy');
        }
        module.initialize();
      }
    })
  ;
  return (returnedValue !== undefined)
    ? returnedValue
    : this
  ;
};

$.fn.accordion.settings = {

  name            : 'Accordion',
  namespace       : 'accordion',

  debug           : false,
  verbose         : false,
  performance     : true,

  exclusive       : true,
  collapsible     : true,
  closeNested     : false,
  animateChildren : true,

  duration        : 350,
  easing          : 'easeOutQuad',

  on              : 'click',

  onOpening       : function(){},
  onOpen          : function(){},
  onClosing       : function(){},
  onClose         : function(){},
  onChange        : function(){},

  error: {
    method : 'The method you called is not defined'
  },

  className   : {
    active    : 'active',
    animating : 'animating'
  },

  selector    : {
    accordion : '.accordion',
    title     : '.title',
    trigger   : '.title',
    content   : '.content'
  }

};

// Adds easing
$.extend( $.easing, {
  easeOutQuad: function (x, t, b, c, d) {
    return -c *(t/=d)*(t-2) + b;
  }
});

})( jQuery, window , document );

<commit_msg>Move accordion settings around<commit_after>/*!
 * # Semantic UI - Accordion
 * http://github.com/semantic-org/semantic-ui/
 *
 *
 * Copyright 2015 Contributors
 * Released under the MIT license
 * http://opensource.org/licenses/MIT
 *
 */

;(function ($, window, document, undefined) {

"use strict";

$.fn.accordion = function(parameters) {
  var
    $allModules     = $(this),

    time            = new Date().getTime(),
    performance     = [],

    query           = arguments[0],
    methodInvoked   = (typeof query == 'string'),
    queryArguments  = [].slice.call(arguments, 1),

    requestAnimationFrame = window.requestAnimationFrame
      || window.mozRequestAnimationFrame
      || window.webkitRequestAnimationFrame
      || window.msRequestAnimationFrame
      || function(callback) { setTimeout(callback, 0); },

    returnedValue
  ;
  $allModules
    .each(function() {
      var
        settings        = ( $.isPlainObject(parameters) )
          ? $.extend(true, {}, $.fn.accordion.settings, parameters)
          : $.extend({}, $.fn.accordion.settings),

        className       = settings.className,
        namespace       = settings.namespace,
        selector        = settings.selector,
        error           = settings.error,

        eventNamespace  = '.' + namespace,
        moduleNamespace = 'module-' + namespace,
        moduleSelector  = $allModules.selector || '',

        $module  = $(this),
        $title   = $module.find(selector.title),
        $content = $module.find(selector.content),

        element  = this,
        instance = $module.data(moduleNamespace),
        observer,
        module
      ;

      module = {

        initialize: function() {
          module.debug('Initializing', $module);
          module.bind.events();
          module.observeChanges();
          module.instantiate();
        },

        instantiate: function() {
          instance = module;
          $module
            .data(moduleNamespace, module)
          ;
        },

        destroy: function() {
          module.debug('Destroying previous instance', $module);
          $module
            .off(eventNamespace)
            .removeData(moduleNamespace)
          ;
        },

        refresh: function() {
          $title   = $module.find(selector.title);
          $content = $module.find(selector.content);
        },

        observeChanges: function() {
          if('MutationObserver' in window) {
            observer = new MutationObserver(function(mutations) {
              module.debug('DOM tree modified, updating selector cache');
              module.refresh();
            });
            observer.observe(element, {
              childList : true,
              subtree   : true
            });
            module.debug('Setting up mutation observer', observer);
          }
        },

        bind: {
          events: function() {
            module.debug('Binding delegated events');
            $module
              .on(settings.on + eventNamespace, selector.trigger, module.event.click)
            ;
          }
        },

        event: {
          click: function() {
            module.toggle.call(this);
          }
        },

        toggle: function(query) {
          var
            $activeTitle = (query !== undefined)
              ? (typeof query === 'number')
                ? $title.eq(query)
                : $(query).closest(selector.title)
              : $(this).closest(selector.title),
            $activeContent = $activeTitle.next($content),
            isAnimating = $activeContent.hasClass(className.animating),
            isActive    = $activeContent.hasClass(className.active),
            isOpen      = (isActive && !isAnimating),
            isOpening   = (!isActive && isAnimating)
          ;
          module.debug('Toggling visibility of content', $activeTitle);
          if(isOpen || isOpening) {
            if(settings.collapsible) {
              module.close.call($activeTitle);
            }
            else {
              module.debug('Cannot close accordion content collapsing is disabled');
            }
          }
          else {
            module.open.call($activeTitle);
          }
        },

        open: function(query) {
          var
            $activeTitle = (query !== undefined)
              ? (typeof query === 'number')
                ? $title.eq(query)
                : $(query).closest(selector.title)
              : $(this).closest(selector.title),
            $activeContent = $activeTitle.next($content),
            isAnimating = $activeContent.hasClass(className.animating),
            isActive    = $activeContent.hasClass(className.active),
            isOpen      = (isActive || isAnimating)
          ;
          if(isOpen) {
            module.debug('Accordion already open, skipping', $activeContent);
            return;
          }
          module.debug('Opening accordion content', $activeTitle);
          settings.onOpening.call($activeContent);
          if(settings.exclusive) {
            module.closeOthers.call($activeTitle);
          }
          $activeTitle
            .addClass(className.active)
          ;
          $activeContent
            .stop(true, true)
            .addClass(className.animating)
          ;
          if(settings.animateChildren) {
            if($.fn.transition !== undefined && $module.transition('is supported')) {
              $activeContent
                .children()
                  .transition({
                    animation   : 'fade in',
                    queue       : false,
                    useFailSafe : true,
                    debug       : settings.debug,
                    verbose     : settings.verbose,
                    duration    : settings.duration
                  })
              ;
            }
            else {
              $activeContent
                .children()
                  .stop(true, true)
                  .animate({
                    opacity: 1
                  }, settings.duration, module.resetOpacity)
              ;
            }
          }
          $activeContent
            .slideDown(settings.duration, settings.easing, function() {
              $activeContent
                .removeClass(className.animating)
                .addClass(className.active)
              ;
              module.reset.display.call(this);
              settings.onOpen.call(this);
              settings.onChange.call(this);
            })
          ;
        },

        close: function(query) {
          var
            $activeTitle = (query !== undefined)
              ? (typeof query === 'number')
                ? $title.eq(query)
                : $(query).closest(selector.title)
              : $(this).closest(selector.title),
            $activeContent = $activeTitle.next($content),
            isAnimating    = $activeContent.hasClass(className.animating),
            isActive       = $activeContent.hasClass(className.active),
            isOpening      = (!isActive && isAnimating),
            isClosing      = (isActive && isAnimating)
          ;
          if((isActive || isOpening) && !isClosing) {
            module.debug('Closing accordion content', $activeContent);
            settings.onClosing.call($activeContent);
            $activeTitle
              .removeClass(className.active)
            ;
            $activeContent
              .stop(true, true)
              .addClass(className.animating)
            ;
            if(settings.animateChildren) {
              if($.fn.transition !== undefined && $module.transition('is supported')) {
                $activeContent
                  .children()
                    .transition({
                      animation   : 'fade out',
                      queue       : false,
                      useFailSafe : true,
                      debug       : settings.debug,
                      verbose     : settings.verbose,
                      duration    : settings.duration
                    })
                ;
              }
              else {
                $activeContent
                  .children()
                    .stop(true, true)
                    .animate({
                      opacity: 0
                    }, settings.duration, module.resetOpacity)
                ;
              }
            }
            $activeContent
              .slideUp(settings.duration, settings.easing, function() {
                $activeContent
                  .removeClass(className.animating)
                  .removeClass(className.active)
                ;
                module.reset.display.call(this);
                settings.onClose.call(this);
                settings.onChange.call(this);
              })
            ;
          }
        },

        closeOthers: function(index) {
          var
            $activeTitle = (index !== undefined)
              ? $title.eq(index)
              : $(this).closest(selector.title),
            $parentTitles    = $activeTitle.parents(selector.content).prev(selector.title),
            $activeAccordion = $activeTitle.closest(selector.accordion),
            activeSelector   = selector.title + '.' + className.active + ':visible',
            activeContent    = selector.content + '.' + className.active + ':visible',
            $openTitles,
            $nestedTitles,
            $openContents
          ;
          if(settings.closeNested) {
            $openTitles   = $activeAccordion.find(activeSelector).not($parentTitles);
            $openContents = $openTitles.next($content);
          }
          else {
            $openTitles   = $activeAccordion.find(activeSelector).not($parentTitles);
            $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles);
            $openTitles   = $openTitles.not($nestedTitles);
            $openContents = $openTitles.next($content);
          }
          if( ($openTitles.length > 0) ) {
            module.debug('Exclusive enabled, closing other content', $openTitles);
            $openTitles
              .removeClass(className.active)
            ;
            $openContents
              .removeClass(className.animating)
              .stop(true, true)
            ;
            if(settings.animateChildren) {
              if($.fn.transition !== undefined && $module.transition('is supported')) {
                $openContents
                  .children()
                    .transition({
                      animation   : 'fade out',
                      useFailSafe : true,
                      debug       : settings.debug,
                      verbose     : settings.verbose,
                      duration    : settings.duration
                    })
                ;
              }
              else {
                $openContents
                  .children()
                    .stop(true, true)
                    .animate({
                      opacity: 0
                    }, settings.duration, module.resetOpacity)
                ;
              }
            }
            $openContents
              .slideUp(settings.duration , settings.easing, function() {
                $(this).removeClass(className.active);
                module.reset.display.call(this);
              })
            ;
          }
        },

        reset: {

          display: function() {
            module.verbose('Removing inline display from element', this);
            $(this).css('display', '');
            if( $(this).attr('style') === '') {
              $(this)
                .attr('style', '')
                .removeAttr('style')
              ;
            }
          },

          opacity: function() {
            module.verbose('Removing inline opacity from element', this);
            $(this).css('opacity', '');
            if( $(this).attr('style') === '') {
              $(this)
                .attr('style', '')
                .removeAttr('style')
              ;
            }
          },

        },

        setting: function(name, value) {
          module.debug('Changing setting', name, value);
          if( $.isPlainObject(name) ) {
            $.extend(true, settings, name);
          }
          else if(value !== undefined) {
            settings[name] = value;
          }
          else {
            return settings[name];
          }
        },
        internal: function(name, value) {
          module.debug('Changing internal', name, value);
          if(value !== undefined) {
            if( $.isPlainObject(name) ) {
              $.extend(true, module, name);
            }
            else {
              module[name] = value;
            }
          }
          else {
            return module[name];
          }
        },
        debug: function() {
          if(settings.debug) {
            if(settings.performance) {
              module.performance.log(arguments);
            }
            else {
              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
              module.debug.apply(console, arguments);
            }
          }
        },
        verbose: function() {
          if(settings.verbose && settings.debug) {
            if(settings.performance) {
              module.performance.log(arguments);
            }
            else {
              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
              module.verbose.apply(console, arguments);
            }
          }
        },
        error: function() {
          module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
          module.error.apply(console, arguments);
        },
        performance: {
          log: function(message) {
            var
              currentTime,
              executionTime,
              previousTime
            ;
            if(settings.performance) {
              currentTime   = new Date().getTime();
              previousTime  = time || currentTime;
              executionTime = currentTime - previousTime;
              time          = currentTime;
              performance.push({
                'Name'           : message[0],
                'Arguments'      : [].slice.call(message, 1) || '',
                'Element'        : element,
                'Execution Time' : executionTime
              });
            }
            clearTimeout(module.performance.timer);
            module.performance.timer = setTimeout(module.performance.display, 500);
          },
          display: function() {
            var
              title = settings.name + ':',
              totalTime = 0
            ;
            time = false;
            clearTimeout(module.performance.timer);
            $.each(performance, function(index, data) {
              totalTime += data['Execution Time'];
            });
            title += ' ' + totalTime + 'ms';
            if(moduleSelector) {
              title += ' \'' + moduleSelector + '\'';
            }
            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
              console.groupCollapsed(title);
              if(console.table) {
                console.table(performance);
              }
              else {
                $.each(performance, function(index, data) {
                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
                });
              }
              console.groupEnd();
            }
            performance = [];
          }
        },
        invoke: function(query, passedArguments, context) {
          var
            object = instance,
            maxDepth,
            found,
            response
          ;
          passedArguments = passedArguments || queryArguments;
          context         = element         || context;
          if(typeof query == 'string' && object !== undefined) {
            query    = query.split(/[\. ]/);
            maxDepth = query.length - 1;
            $.each(query, function(depth, value) {
              var camelCaseValue = (depth != maxDepth)
                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
                : query
              ;
              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
                object = object[camelCaseValue];
              }
              else if( object[camelCaseValue] !== undefined ) {
                found = object[camelCaseValue];
                return false;
              }
              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
                object = object[value];
              }
              else if( object[value] !== undefined ) {
                found = object[value];
                return false;
              }
              else {
                module.error(error.method, query);
                return false;
              }
            });
          }
          if ( $.isFunction( found ) ) {
            response = found.apply(context, passedArguments);
          }
          else if(found !== undefined) {
            response = found;
          }
          if($.isArray(returnedValue)) {
            returnedValue.push(response);
          }
          else if(returnedValue !== undefined) {
            returnedValue = [returnedValue, response];
          }
          else if(response !== undefined) {
            returnedValue = response;
          }
          return found;
        }
      };
      if(methodInvoked) {
        if(instance === undefined) {
          module.initialize();
        }
        module.invoke(query);
      }
      else {
        if(instance !== undefined) {
          instance.invoke('destroy');
        }
        module.initialize();
      }
    })
  ;
  return (returnedValue !== undefined)
    ? returnedValue
    : this
  ;
};

$.fn.accordion.settings = {

  name            : 'Accordion',
  namespace       : 'accordion',

  debug           : false,
  verbose         : false,
  performance     : true,

  on              : 'click',

  exclusive       : true,
  collapsible     : true,
  closeNested     : false,
  animateChildren : true,

  duration        : 350,
  easing          : 'easeOutQuad',

  onOpening       : function(){},
  onOpen          : function(){},
  onClosing       : function(){},
  onClose         : function(){},
  onChange        : function(){},

  error: {
    method : 'The method you called is not defined'
  },

  className   : {
    active    : 'active',
    animating : 'animating'
  },

  selector    : {
    accordion : '.accordion',
    title     : '.title',
    trigger   : '.title',
    content   : '.content'
  }

};

// Adds easing
$.extend( $.easing, {
  easeOutQuad: function (x, t, b, c, d) {
    return -c *(t/=d)*(t-2) + b;
  }
});

})( jQuery, window , document );

<|endoftext|>