<commit_before>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

define('Sage/Platform/Mobile/Views/Signature', [
    'dojo/_base/declare',
    'dojo/_base/json',
    'dojo/query',
    'dojo/dom-geometry',
    'dojo/window',
    '../Format',
    '../_TemplatedWidgetMixin',
    '../View'
], function(
    declare,
    json,
    query,
    domGeom,
    win,
    format,
    _TemplatedWidgetMixin,
    View
) {

    return declare('Sage.Platform.Mobile.Views.Signature', [View, _TemplatedWidgetMixin], {
        // Localization
        titleText: 'Signature',
        clearCanvasText: 'Erase',
        undoText: 'Undo',

        //Templates
        widgetTemplate: new Simplate([
            '
',
                '{%! $.canvasTemplate %}',
                '
',
                    '<button class="button" data-action="_undo">{%: $.undoText %}</button>',
                    '<button class="button" data-action="clearValue">{%: $.clearCanvasText %}</button>',
                '
',
            '
'
        ]),
        canvasTemplate: new Simplate([
            '<canvas class="signature-canvas" data-dojo-attach-point="signatureNode" width="{%: $.canvasNodeWidth %}" height="{%: $.canvasNodeHeight %}" data-dojo-attach-event="onmousedown:_penDown,onmousemove:_penMove,onmouseup:_penUp,ontouchstart:_penDown,ontouchmove:_penMove,ontouchend:_penUp"></canvas>'
        ]),
        signatureNode: null,

        //View Properties
        id: 'signature_edit',
        tier: 1,
        events: {
            'click': true
        },
        expose: false,
        signature: [],
        trace: [],
        lastpos: {x:-1, y:-1},
        config: {
            scale: 1,
            lineWidth: 3,
            penColor: 'blue',
            drawColor: 'red'
        },
        isPenDown: false,
        context: null,
        buffer: [],
        canvasNodeWidth: 360, // starting default size
        canvasNodeHeight: 120, // adjusted on show()

        startup: function() {
            this.inherited(arguments);

            this.subscribe('/app/resize', this.onResize);
            this.context = this.signatureNode.getContext('2d');
        },
        onBeforeTransitionTo: function() {
            this.inherited(arguments);
            var options = this.options;

            if (options && options.lineWidth) { this.config.lineWidth = options.lineWidth; }
            if (options && options.penColor)  { this.config.penColor  = options.penColor;  }
            if (options && options.drawColor) { this.config.drawColor = options.drawColor; }
            this.signature = (options && options.signature) ? options.signature : [];

            this._sizeCanvas();
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        getValues: function() {
            return json.toJson(this.optimizeSignature());
        },
        setValue: function(val, initial) {
            this.signature = val ? json.fromJson(val) : [];
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        clearValue: function() {
            this.buffer = this.signature;
            this.setValue('', true);
        },
        // _getCoords returns pointer pixel coordinates [x,y] relative to canvas object
        _getCoords: function (e) {
            var offset = domGeom.position(this.signatureNode, false);
            return e.touches
                ? [
                    e.touches[0].pageX - offset.x,
                    e.touches[0].pageY - offset.y
                  ]
                : [
                    e.clientX - offset.x,
                    e.clientY - offset.y
                  ]
                ;
        },
        _penDown: function (e) {
            this.isPenDown = true;
            this.lastpos = this._getCoords(e);
            this.context.lineWidth = this.config.lineWidth;
            this.context.strokeStyle = this.config.drawColor;
            e.preventDefault();
        },
        _penMove: function (e) {
            if (!this.isPenDown) { return; }
            this.pos = this._getCoords(e);
            e.preventDefault();
            this.context.beginPath();
            this.context.moveTo(this.lastpos[0], this.lastpos[1]);
            this.context.lineTo(this.pos[0], this.pos[1]);
            this.context.closePath();
            this.context.stroke();
            e.preventDefault();
            this.lastpos = this.pos;
            this.trace.push(this.pos);
        },
        _penUp: function (e) {
            e.preventDefault();
            this.isPenDown = false;
            if (this.trace.length)
                this.signature.push(this.trace);

            this.trace = [];
            this.context.strokeStyle = this.config.penColor;
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        _undo: function () {
            if (this.signature.length)
            {
                this.buffer = this.signature.pop();
                if (!this.signature.length)
                    this.buffer = [this.buffer];

            } else if (this.buffer.length)
            {
                this.signature = this.buffer;
            }
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        _sizeCanvas: function () {
            this.canvasNodeWidth  = Math.floor(win.getBox().w * 0.92);

            this.canvasNodeHeight = Math.min(
                Math.floor(this.canvasNodeWidth * 0.5),
                win.getBox().h - query('.toolbar')[0].offsetHeight - query('.toolbar-bottom')[0].offsetHeight
            );

            this.signatureNode.width  = this.canvasNodeWidth;
            this.signatureNode.height = this.canvasNodeHeight;
        },
        onResize: function (e) {
            var newScale,
                oldWidth  = this.canvasNodeWidth,
                oldHeight = this.canvasNodeHeight;
            this._sizeCanvas();

            newScale = Math.min(
                this.canvasNodeWidth  / oldWidth,
                this.canvasNodeHeight / oldHeight
            );

            this.signature = this.rescale(newScale);
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        redraw: function (vector, canvas, options) {
            format.canvasDraw(vector, canvas, options);
        },
        rescale: function (scale) {
            var rescaled = [];
            for (var i = 0; i < this.signature.length; i++)
            {
                rescaled.push([]);
                for (var j = 0; j < this.signature[i].length; j++)
                {
                    rescaled[i].push([
                        this.signature[i][j][0] * scale,
                        this.signature[i][j][1] * scale
                    ])
                }
            }
            return rescaled;
        },
        optimizeSignature: function() {
            var optimized = [];

            for (var i = 0; i < this.signature.length; i++)
                optimized.push(this.optimize(this.signature[i]))

            return optimized;
        },
        optimize: function(vector) {
            if (vector.length < 2) return vector;

            var result = [],
                minA = 0.95,
                maxL = 15.0, // 15.0, 10.0 works well
                rootP = vector[0],
                lastP = vector[1],
                rootV = [lastP[0] - rootP[0], lastP[1] - rootP[1]],
                rootL = Math.sqrt(rootV[0]*rootV[0] + rootV[1]*rootV[1]),
                currentP,
                currentV,
                currentL,
                dotProduct;

            for (var i = 2; i < vector.length; i++)
            {
                currentP = vector[i];
                currentV = [currentP[0] - rootP[0], currentP[1] - rootP[1]];
                currentL = Math.sqrt(currentV[0]*currentV[0] + currentV[1]*currentV[1]);
                dotProduct = (rootV[0]*currentV[0] + rootV[1]*currentV[1]) / (rootL*currentL);

                if (dotProduct < minA || currentL > maxL)
                {
                    result.push(rootP);

                    rootP = lastP;
                    lastP = currentP;
                    rootV = [lastP[0] - rootP[0], lastP[1] - rootP[1]];
                    rootL = Math.sqrt(rootV[0]*rootV[0] + rootV[1]*rootV[1]);
                }
                else
                {
                    lastP = currentP;
                }

            }

            result.push(lastP);

            return result;
        }
    });
});<commit_msg>abtracted calculating width/height for apps to customize the algorithm<commit_after>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

define('Sage/Platform/Mobile/Views/Signature', [
    'dojo/_base/declare',
    'dojo/_base/json',
    'dojo/query',
    'dojo/dom-geometry',
    'dojo/window',
    '../Format',
    '../_TemplatedWidgetMixin',
    '../View'
], function(
    declare,
    json,
    query,
    domGeom,
    win,
    format,
    _TemplatedWidgetMixin,
    View
) {

    return declare('Sage.Platform.Mobile.Views.Signature', [View, _TemplatedWidgetMixin], {
        // Localization
        titleText: 'Signature',
        clearCanvasText: 'Erase',
        undoText: 'Undo',

        //Templates
        widgetTemplate: new Simplate([
            '
',
                '{%! $.canvasTemplate %}',
                '
',
                    '<button class="button" data-action="_undo">{%: $.undoText %}</button>',
                    '<button class="button" data-action="clearValue">{%: $.clearCanvasText %}</button>',
                '
',
            '
'
        ]),
        canvasTemplate: new Simplate([
            '<canvas class="signature-canvas" data-dojo-attach-point="signatureNode" width="{%: $.canvasNodeWidth %}" height="{%: $.canvasNodeHeight %}" data-dojo-attach-event="onmousedown:_penDown,onmousemove:_penMove,onmouseup:_penUp,ontouchstart:_penDown,ontouchmove:_penMove,ontouchend:_penUp"></canvas>'
        ]),
        signatureNode: null,

        //View Properties
        id: 'signature_edit',
        tier: 1,
        events: {
            'click': true
        },
        expose: false,
        signature: [],
        trace: [],
        lastpos: {x:-1, y:-1},
        config: {
            scale: 1,
            lineWidth: 3,
            penColor: 'blue',
            drawColor: 'red'
        },
        isPenDown: false,
        context: null,
        buffer: [],
        canvasNodeWidth: 360, // starting default size
        canvasNodeHeight: 120, // adjusted on show()

        startup: function() {
            this.inherited(arguments);

            this.subscribe('/app/resize', this.onResize);
            this.context = this.signatureNode.getContext('2d');
        },
        onBeforeTransitionTo: function() {
            this.inherited(arguments);
            var options = this.options;

            if (options && options.lineWidth) { this.config.lineWidth = options.lineWidth; }
            if (options && options.penColor)  { this.config.penColor  = options.penColor;  }
            if (options && options.drawColor) { this.config.drawColor = options.drawColor; }
            this.signature = (options && options.signature) ? options.signature : [];

            this._sizeCanvas();
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        getValues: function() {
            return json.toJson(this.optimizeSignature());
        },
        setValue: function(val, initial) {
            this.signature = val ? json.fromJson(val) : [];
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        clearValue: function() {
            this.buffer = this.signature;
            this.setValue('', true);
        },
        // _getCoords returns pointer pixel coordinates [x,y] relative to canvas object
        _getCoords: function (e) {
            var offset = domGeom.position(this.signatureNode, false);
            return e.touches
                ? [
                    e.touches[0].pageX - offset.x,
                    e.touches[0].pageY - offset.y
                  ]
                : [
                    e.clientX - offset.x,
                    e.clientY - offset.y
                  ]
                ;
        },
        _penDown: function (e) {
            this.isPenDown = true;
            this.lastpos = this._getCoords(e);
            this.context.lineWidth = this.config.lineWidth;
            this.context.strokeStyle = this.config.drawColor;
            e.preventDefault();
        },
        _penMove: function (e) {
            if (!this.isPenDown) { return; }
            this.pos = this._getCoords(e);
            e.preventDefault();
            this.context.beginPath();
            this.context.moveTo(this.lastpos[0], this.lastpos[1]);
            this.context.lineTo(this.pos[0], this.pos[1]);
            this.context.closePath();
            this.context.stroke();
            e.preventDefault();
            this.lastpos = this.pos;
            this.trace.push(this.pos);
        },
        _penUp: function (e) {
            e.preventDefault();
            this.isPenDown = false;
            if (this.trace.length)
                this.signature.push(this.trace);

            this.trace = [];
            this.context.strokeStyle = this.config.penColor;
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        _undo: function () {
            if (this.signature.length)
            {
                this.buffer = this.signature.pop();
                if (!this.signature.length)
                    this.buffer = [this.buffer];

            } else if (this.buffer.length)
            {
                this.signature = this.buffer;
            }
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        _sizeCanvas: function () {
            this.canvasNodeWidth  = this.calculateWidth();
            this.canvasNodeHeight = this.calculateHeight();

            this.signatureNode.width  = this.canvasNodeWidth;
            this.signatureNode.height = this.canvasNodeHeight;
        },
        calculateWidth: function() {
            return Math.floor(win.getBox().w * 0.92);
        },
        calculateHeight: function() {
            return Math.min(Math.floor(this.canvasNodeWidth * 0.5),
                            win.getBox().h - query('.toolbar')[0].offsetHeight - query('.toolbar-bottom')[0].offsetHeight
                        );
        },
        onResize: function (e) {
            var newScale,
                oldWidth  = this.canvasNodeWidth,
                oldHeight = this.canvasNodeHeight;
            this._sizeCanvas();

            newScale = Math.min(
                this.canvasNodeWidth  / oldWidth,
                this.canvasNodeHeight / oldHeight
            );

            this.signature = this.rescale(newScale);
            this.redraw(this.signature, this.signatureNode, this.config);
        },
        redraw: function (vector, canvas, options) {
            format.canvasDraw(vector, canvas, options);
        },
        rescale: function (scale) {
            var rescaled = [];
            for (var i = 0; i < this.signature.length; i++)
            {
                rescaled.push([]);
                for (var j = 0; j < this.signature[i].length; j++)
                {
                    rescaled[i].push([
                        this.signature[i][j][0] * scale,
                        this.signature[i][j][1] * scale
                    ])
                }
            }
            return rescaled;
        },
        optimizeSignature: function() {
            var optimized = [];

            for (var i = 0; i < this.signature.length; i++)
                optimized.push(this.optimize(this.signature[i]))

            return optimized;
        },
        optimize: function(vector) {
            if (vector.length < 2) return vector;

            var result = [],
                minA = 0.95,
                maxL = 15.0, // 15.0, 10.0 works well
                rootP = vector[0],
                lastP = vector[1],
                rootV = [lastP[0] - rootP[0], lastP[1] - rootP[1]],
                rootL = Math.sqrt(rootV[0]*rootV[0] + rootV[1]*rootV[1]),
                currentP,
                currentV,
                currentL,
                dotProduct;

            for (var i = 2; i < vector.length; i++)
            {
                currentP = vector[i];
                currentV = [currentP[0] - rootP[0], currentP[1] - rootP[1]];
                currentL = Math.sqrt(currentV[0]*currentV[0] + currentV[1]*currentV[1]);
                dotProduct = (rootV[0]*currentV[0] + rootV[1]*currentV[1]) / (rootL*currentL);

                if (dotProduct < minA || currentL > maxL)
                {
                    result.push(rootP);

                    rootP = lastP;
                    lastP = currentP;
                    rootV = [lastP[0] - rootP[0], lastP[1] - rootP[1]];
                    rootL = Math.sqrt(rootV[0]*rootV[0] + rootV[1]*rootV[1]);
                }
                else
                {
                    lastP = currentP;
                }

            }

            result.push(lastP);

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


<commit_before>jest.unmock('redux-saga');
jest.unmock('redux-saga/effects');

jest.unmock('../constants/ActionTypes');
jest.unmock('../constants/ContentTypes');
jest.unmock('../actionCreators');
jest.unmock('../sagas');
jest.unmock('../fetchContent');

import { put, call, select } from 'redux-saga/effects';

import ContentTypes from '../constants/ContentTypes';
import fetchContent from '../fetchContent';
import { configSelector, fetchResource } from '../sagas';

import {
  createRequest,
  startRequest
} from '../actionCreators';

const contentType = ContentTypes.POST;
const createRequestOptions = { params: { id: 1337 }};

describe('sagas', () => {
  const generator = fetchResource(
    createRequest(contentType, createRequestOptions)
  );

  it('yields a select using configuration selector', () => {
    expect(generator.next().value).toEqual(select(configSelector));
  });

  it('puts a create request action', () => {
    expect(generator.next().value).toEqual(put(startRequest(contentType)));
  });

  it('yields a call to fetchContent with ', () => {
    expect(generator.next().value).toEqual(
      call(fetchContent, contentType, createRequestOptions, undefined, {})
    );
  });
});
<commit_msg>Unmock fetchContnet deps;<commit_after>jest.unmock('redux-saga');
jest.unmock('redux-saga/effects');
jest.unmock('lodash.merge');
jest.unmock('invariant');

jest.unmock('../constants/ActionTypes');
jest.unmock('../constants/ContentTypes');
jest.unmock('../actionCreators');
jest.unmock('../sagas');
jest.unmock('../fetchContent');

import { put, call, select } from 'redux-saga/effects';

import ContentTypes from '../constants/ContentTypes';
import fetchContent from '../fetchContent';
import { configSelector, fetchResource } from '../sagas';

import {
  createRequest,
  startRequest
} from '../actionCreators';

const contentType = ContentTypes.POST;
const createRequestOptions = { params: { id: 1337 }};

describe('sagas', () => {
  const generator = fetchResource(
    createRequest(contentType, createRequestOptions)
  );

  it('yields a select using configuration selector', () => {
    expect(generator.next().value).toEqual(select(configSelector));
  });

  it('puts a create request action', () => {
    expect(generator.next().value).toEqual(put(startRequest(contentType)));
  });

  it('yields a call to fetchContent with ', () => {
    expect(generator.next().value).toEqual(
      call(fetchContent, contentType, createRequestOptions, undefined, {})
    );
  });
});
<|endoftext|>


<commit_before>(function(define) {'use strict';
define(function(require) {

return function(Variable) {

   function LogicalVar(values, options) {
      this.values = new Variable.Vector(values).mutable(true);
   }

   LogicalVar.prototype = Object.create(Variable.prototype);

   LogicalVar.prototype.asScalar = function asScalar() {
      return new Variable(this.values.map(function(val) {
         return val === true ? 1
              : val === false ? 0
                              : null;
      }));
   };

   return LogicalVar;
};

});

}(typeof define === 'function' && define.amd ? define : function(factory) {
   'use strict';
   module.exports = factory(require);
}));
<commit_msg>Add LogicalVar#which.<commit_after>(function(define) {'use strict';
define(function(require) {

return function(Variable) {

   function LogicalVar(values, options) {
      this.values = new Variable.Vector(values).mutable(true);
   }

   LogicalVar.prototype = Object.create(Variable.prototype);

   LogicalVar.prototype.asScalar = function asScalar() {
      return new Variable(this.values.map(function(val) {
         return val === true ? 1
              : val === false ? 0
                              : null;
      }));
   };

   LogicalVar.prototype.which = function which() {
      // `false` -> 0; `true` -> the array index plus 1; and NA -> NA
      var arr;
      arr = this.values.toArray().map(function(v, k) {
         return v === false ? 0 : v && k + 1;
      }).filter(function(v) { return v !== 0; });
      return new Variable(arr);
   };

   return LogicalVar;
};

});

}(typeof define === 'function' && define.amd ? define : function(factory) {
   'use strict';
   module.exports = factory(require);
}));
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
//  Copyright (C) 2011  The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// Keyboard management
//============================================================================

IPython.namespace('IPython.keyboard');

IPython.keyboard = (function (IPython) {
    "use strict";

    // Setup global keycodes and inverse keycodes.

    // See http://unixpapa.com/js/key.html for a complete description. The short of
    // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
    // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
    // but have minor differences.

    // These apply to Firefox, (Webkit and IE)
    var _keycodes = {
        'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
        'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
        's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
        '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
        '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
        '[ {': 219, '] }': 221, '` ~': 192,  ', <': 188, '. >': 190, '/ ?': 191,
        '\\ |': 220, '\' "': 222,
        'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
        'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
        'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
        'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
        'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
        'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
        'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
        'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
        'insert': 45, 'delete': 46, 'numlock': 144,
    };

    // These apply to Firefox and Opera
    var _mozilla_keycodes = {
        '; :': 59, '= +': 61, '- _': 173, 'meta': 224
    }

    // This apply to Webkit and IE
    var _ie_keycodes = {
        '; :': 186, '= +': 187, '- _': 189,
    }

    var browser = IPython.utils.browser[0];
    var platform = IPython.utils.platform;

    if (browser === 'Firefox' || browser === 'Opera') {
        $.extend(_keycodes, _mozilla_keycodes);
    } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
        $.extend(_keycodes, _ie_keycodes);
    }

    var keycodes = {};
    var inv_keycodes = {};
    for (var name in _keycodes) {
        var names = name.split(' ');
        if (names.length === 1) {
            var n = names[0]
            keycodes[n] = _keycodes[n]
            inv_keycodes[_keycodes[n]] = n
        } else {
            var primary = names[0];
            var secondary = names[1];
            keycodes[primary] = _keycodes[name]
            keycodes[secondary] = _keycodes[name]
            inv_keycodes[_keycodes[name]] = primary
        }
    }

    var normalize_key = function (key) {
        return inv_keycodes[keycodes[key]];
    }

    var normalize_shortcut = function (shortcut) {
        // Put a shortcut into normalized form:
        // 1. Make lowercase
        // 2. Replace cmd by meta
        // 3. Sort '+' separated modifiers into the order alt+ctrl+meta+shift
        // 4. Normalize keys
        shortcut = shortcut.toLowerCase().replace('cmd', 'meta');
        var values = shortcut.split("+");
        if (values.length === 1) {
            return normalize_key(values[0])
        } else {
            var modifiers = values.slice(0,-1);
            var key = normalize_key(values[values.length-1]);
            modifiers.sort();
            return modifiers.join('+') + '+' + key;
        }
    }

    var shortcut_to_event = function (shortcut, type) {
        // Convert a shortcut (shift+r) to a jQuery Event object
        type = type || 'keydown';
        shortcut = normalize_shortcut(shortcut);
        var values = shortcut.split("+");
        var modifiers = values.slice(0,-1);
        var key = values[values.length-1];
        var opts = {which: keycodes[key]};
        if (modifiers.indexOf('alt') !== -1) {opts.altKey = true;}
        if (modifiers.indexOf('ctrl') !== -1) {opts.ctrlKey = true;}
        if (modifiers.indexOf('meta') !== -1) {opts.metaKey = true;}
        if (modifiers.indexOf('shift') !== -1) {opts.shiftKey = true;}
        return $.Event(type, opts);
    }

    var event_to_shortcut = function (event) {
        // Convert a jQuery Event object to a shortcut (shift+r)
        var shortcut = '';
        var key = inv_keycodes[event.which]
        if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
        if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
        if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
        if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
        shortcut += key;
        return shortcut
    }

    var trigger_keydown = function (shortcut, element) {
        // Trigger shortcut keydown on an element
        element = element || document;
        element = $(element);
        var event = shortcut_to_event(shortcut, 'keydown');
        element.trigger(event);
    }

    // Shortcut manager class

    var ShortcutManager = function (delay) {
        this._shortcuts = {}
        this._counts = {}
        this._timers = {}
        this.delay = delay || 800; // delay in milliseconds
    }

    ShortcutManager.prototype.help = function () {
        var help = [];
        for (var shortcut in this._shortcuts) {
            var help_string = this._shortcuts[shortcut]['help'];
            var help_index = this._shortcuts[shortcut]['help_index'];
            if (help_string) {
                if (platform === 'MacOS') {
                    shortcut = shortcut.replace('meta', 'cmd');
                }
                help.push({
                    shortcut: shortcut,
                    help: help_string,
                    help_index: help_index}
                );
            }
        }
        help.sort(function (a, b) {
            if (a.help_index > b.help_index)
                return 1;
            if (a.help_index < b.help_index)
                return -1;
            return 0;
        });
        return help;
    }

    ShortcutManager.prototype.clear_shortcuts = function () {
        this._shortcuts = {};
    }

    ShortcutManager.prototype.add_shortcut = function (shortcut, data, suppress_help_update) {
        if (typeof(data) === 'function') {
            data = {help: '', help_index: '', handler: data}
        }
        data.help_index = data.help_index || '';
        data.help = data.help || '';
        data.count = data.count || 1;
        if (data.help_index === '') {
            data.help_index = 'zz';
        }
        shortcut = normalize_shortcut(shortcut);
        this._counts[shortcut] = 0;
        this._shortcuts[shortcut] = data;
        if (!suppress_help_update) {
            // update the keyboard shortcuts notebook help
            $([IPython.events]).trigger('rebuild.QuickHelp')
        }
    }

    ShortcutManager.prototype.add_shortcuts = function (data) {
        for (var shortcut in data) {
            this.add_shortcut(shortcut, data[shortcut], true);
        }
        // update the keyboard shortcuts notebook help
        IPython.quick_help = new IPython.QuickHelp();
    }

    ShortcutManager.prototype.remove_shortcut = function (shortcut, suppress_help_update) {
        shortcut = normalize_shortcut(shortcut);
        delete this._counts[shortcut];
        delete this._shortcuts[shortcut];
        // update the keyboard shortcuts notebook help
        IPython.quick_help = new IPython.QuickHelp();
        if (!suppress_help_update) {
            // update the keyboard shortcuts notebook help
            $([IPython.events]).trigger('rebuild.QuickHelp');
        }
    }

    ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
        var that = this;
        var c = this._counts;
        var t = this._timers;
        var timer = null;
        if (c[shortcut] === data.count-1) {
            c[shortcut] = 0;
            var timer = t[shortcut];
            if (timer) {clearTimeout(timer); delete t[shortcut];}
            return data.handler(event);
        } else {
            c[shortcut] = c[shortcut] + 1;
            timer = setTimeout(function () {
                c[shortcut] = 0;
            }, that.delay);
            t[shortcut] = timer;
        }
        return false;
    }

    ShortcutManager.prototype.call_handler = function (event) {
        var shortcut = event_to_shortcut(event);
        var data = this._shortcuts[shortcut];
        if (data) {
            var handler = data['handler'];
            if (handler) {
                if (data.count === 1) {
                    return handler(event);
                } else if (data.count > 1) {
                    return this.count_handler(shortcut, event, data);
                }
            }
        }
        return true;
    }

    return {
        keycodes : keycodes,
        inv_keycodes : inv_keycodes,
        ShortcutManager : ShortcutManager,
        normalize_key : normalize_key,
        normalize_shortcut : normalize_shortcut,
        shortcut_to_event : shortcut_to_event,
        event_to_shortcut : event_to_shortcut,
        trigger_keydown : trigger_keydown
    }

}(IPython));
<commit_msg>remove recreation of QuickHelp<commit_after>//----------------------------------------------------------------------------
//  Copyright (C) 2011  The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// Keyboard management
//============================================================================

IPython.namespace('IPython.keyboard');

IPython.keyboard = (function (IPython) {
    "use strict";

    // Setup global keycodes and inverse keycodes.

    // See http://unixpapa.com/js/key.html for a complete description. The short of
    // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
    // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
    // but have minor differences.

    // These apply to Firefox, (Webkit and IE)
    var _keycodes = {
        'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
        'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
        's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
        '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
        '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
        '[ {': 219, '] }': 221, '` ~': 192,  ', <': 188, '. >': 190, '/ ?': 191,
        '\\ |': 220, '\' "': 222,
        'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
        'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
        'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
        'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
        'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
        'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
        'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
        'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
        'insert': 45, 'delete': 46, 'numlock': 144,
    };

    // These apply to Firefox and Opera
    var _mozilla_keycodes = {
        '; :': 59, '= +': 61, '- _': 173, 'meta': 224
    }

    // This apply to Webkit and IE
    var _ie_keycodes = {
        '; :': 186, '= +': 187, '- _': 189,
    }

    var browser = IPython.utils.browser[0];
    var platform = IPython.utils.platform;

    if (browser === 'Firefox' || browser === 'Opera') {
        $.extend(_keycodes, _mozilla_keycodes);
    } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
        $.extend(_keycodes, _ie_keycodes);
    }

    var keycodes = {};
    var inv_keycodes = {};
    for (var name in _keycodes) {
        var names = name.split(' ');
        if (names.length === 1) {
            var n = names[0]
            keycodes[n] = _keycodes[n]
            inv_keycodes[_keycodes[n]] = n
        } else {
            var primary = names[0];
            var secondary = names[1];
            keycodes[primary] = _keycodes[name]
            keycodes[secondary] = _keycodes[name]
            inv_keycodes[_keycodes[name]] = primary
        }
    }

    var normalize_key = function (key) {
        return inv_keycodes[keycodes[key]];
    }

    var normalize_shortcut = function (shortcut) {
        // Put a shortcut into normalized form:
        // 1. Make lowercase
        // 2. Replace cmd by meta
        // 3. Sort '+' separated modifiers into the order alt+ctrl+meta+shift
        // 4. Normalize keys
        shortcut = shortcut.toLowerCase().replace('cmd', 'meta');
        var values = shortcut.split("+");
        if (values.length === 1) {
            return normalize_key(values[0])
        } else {
            var modifiers = values.slice(0,-1);
            var key = normalize_key(values[values.length-1]);
            modifiers.sort();
            return modifiers.join('+') + '+' + key;
        }
    }

    var shortcut_to_event = function (shortcut, type) {
        // Convert a shortcut (shift+r) to a jQuery Event object
        type = type || 'keydown';
        shortcut = normalize_shortcut(shortcut);
        var values = shortcut.split("+");
        var modifiers = values.slice(0,-1);
        var key = values[values.length-1];
        var opts = {which: keycodes[key]};
        if (modifiers.indexOf('alt') !== -1) {opts.altKey = true;}
        if (modifiers.indexOf('ctrl') !== -1) {opts.ctrlKey = true;}
        if (modifiers.indexOf('meta') !== -1) {opts.metaKey = true;}
        if (modifiers.indexOf('shift') !== -1) {opts.shiftKey = true;}
        return $.Event(type, opts);
    }

    var event_to_shortcut = function (event) {
        // Convert a jQuery Event object to a shortcut (shift+r)
        var shortcut = '';
        var key = inv_keycodes[event.which]
        if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
        if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
        if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
        if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
        shortcut += key;
        return shortcut
    }

    var trigger_keydown = function (shortcut, element) {
        // Trigger shortcut keydown on an element
        element = element || document;
        element = $(element);
        var event = shortcut_to_event(shortcut, 'keydown');
        element.trigger(event);
    }

    // Shortcut manager class

    var ShortcutManager = function (delay) {
        this._shortcuts = {}
        this._counts = {}
        this._timers = {}
        this.delay = delay || 800; // delay in milliseconds
    }

    ShortcutManager.prototype.help = function () {
        var help = [];
        for (var shortcut in this._shortcuts) {
            var help_string = this._shortcuts[shortcut]['help'];
            var help_index = this._shortcuts[shortcut]['help_index'];
            if (help_string) {
                if (platform === 'MacOS') {
                    shortcut = shortcut.replace('meta', 'cmd');
                }
                help.push({
                    shortcut: shortcut,
                    help: help_string,
                    help_index: help_index}
                );
            }
        }
        help.sort(function (a, b) {
            if (a.help_index > b.help_index)
                return 1;
            if (a.help_index < b.help_index)
                return -1;
            return 0;
        });
        return help;
    }

    ShortcutManager.prototype.clear_shortcuts = function () {
        this._shortcuts = {};
    }

    ShortcutManager.prototype.add_shortcut = function (shortcut, data, suppress_help_update) {
        if (typeof(data) === 'function') {
            data = {help: '', help_index: '', handler: data}
        }
        data.help_index = data.help_index || '';
        data.help = data.help || '';
        data.count = data.count || 1;
        if (data.help_index === '') {
            data.help_index = 'zz';
        }
        shortcut = normalize_shortcut(shortcut);
        this._counts[shortcut] = 0;
        this._shortcuts[shortcut] = data;
        if (!suppress_help_update) {
            // update the keyboard shortcuts notebook help
            $([IPython.events]).trigger('rebuild.QuickHelp')
        }
    }

    ShortcutManager.prototype.add_shortcuts = function (data) {
        for (var shortcut in data) {
            this.add_shortcut(shortcut, data[shortcut], true);
        }
        // update the keyboard shortcuts notebook help
        $([IPython.events]).trigger('rebuild.QuickHelp');
    }

    ShortcutManager.prototype.remove_shortcut = function (shortcut, suppress_help_update) {
        shortcut = normalize_shortcut(shortcut);
        delete this._counts[shortcut];
        delete this._shortcuts[shortcut];
        if (!suppress_help_update) {
            // update the keyboard shortcuts notebook help
            $([IPython.events]).trigger('rebuild.QuickHelp');
        }
    }

    ShortcutManager.prototype.count_handler = function (shortcut, event, data) {
        var that = this;
        var c = this._counts;
        var t = this._timers;
        var timer = null;
        if (c[shortcut] === data.count-1) {
            c[shortcut] = 0;
            var timer = t[shortcut];
            if (timer) {clearTimeout(timer); delete t[shortcut];}
            return data.handler(event);
        } else {
            c[shortcut] = c[shortcut] + 1;
            timer = setTimeout(function () {
                c[shortcut] = 0;
            }, that.delay);
            t[shortcut] = timer;
        }
        return false;
    }

    ShortcutManager.prototype.call_handler = function (event) {
        var shortcut = event_to_shortcut(event);
        var data = this._shortcuts[shortcut];
        if (data) {
            var handler = data['handler'];
            if (handler) {
                if (data.count === 1) {
                    return handler(event);
                } else if (data.count > 1) {
                    return this.count_handler(shortcut, event, data);
                }
            }
        }
        return true;
    }

    return {
        keycodes : keycodes,
        inv_keycodes : inv_keycodes,
        ShortcutManager : ShortcutManager,
        normalize_key : normalize_key,
        normalize_shortcut : normalize_shortcut,
        shortcut_to_event : shortcut_to_event,
        event_to_shortcut : event_to_shortcut,
        trigger_keydown : trigger_keydown
    }

}(IPython));
<|endoftext|>


<commit_before>const EXPORT = ["HttpWatcher"];

var HttpWatcher = shared.get("HttpWatcher") || {
    // 監視するホスト
    targetHostsArray: [
        B_HOST,
        //"bbeta.hatena.ne.jp",
        //"localhost",
    ],

    get targetHosts HW_get_targetHosts() {
        let hosts = this.targetHostsArray.reduce(function (hosts, host) {
            hosts[host] = true;
            return hosts;
        }, new DictionaryObject());
        delete this.targetHosts;
        return this.targetHosts = hosts;
    },

    onEditBookmark: function HW_onEditBookmark(channel) {
        let url = this._getResponseHeader(channel, "X-Bookmark-URL");
        if (!url) return;
        let data = this._getPostData(channel);
        if (!data) return;

        // ブックマーク成功したら、sync する
        // これにより、リモートとのデータの同期がとれる
        // XXX: Sync に依存してしまう
        let listener = Sync.createListener("complete", function onSync() {
            p('Sync completed');
            listener.unlisten();
            HTTPCache.entry.cache.clear(data.url);
            if (!Model.Bookmark.findByUrl(data.url).length) {
                p(data.url + ' is not registered.  Retry sync.');
                // 同期が間に合わなかったら少し待ってもう一度だけ同期する。
                setTimeout(method(Sync, 'sync'), 2000);
            }
        }, null, 0, false);
        Sync.sync();

        let bookmark = Model.Bookmark.findByUrl(data.url)[0];
        if (bookmark) {
            // すでに存在するブックマークは Sync で
            // 同期できないので、ここで DB を更新しておく。
            if (data.title)
                bookmark.title = data.title;
            bookmark.comment = data.comment;
            bookmark.save();
            EventService.dispatch('BookmarksUpdated');
        }
    },

    onDeleteBookmarks: function HW_onDeleteBookmarks(channel) {
        if (channel.responseStatus !== 200) return;
        let data = this._getPostData(channel);
        let url = this._getResponseHeader(channel, "X-Bookmark-URL") || data.url;
        let urls = url ? [url] : data.urllist ? data.urllist.split("|") : null;
        if (!urls) return;
        Model.Bookmark.deleteByURLs(urls);
    },

    onEditTag: function HW_onEditTag(channel) {
        switch (channel.responseStatus) {
        case 200:
            // JSON API の成功
            break;

        // タグ編集ページ (/user_id/tag?tag=...) から編集した場合、
        // レスポンスはタグページまたはホームページへのリダイレクトになる。
        case 302:
            let location = this._getResponseHeader(channel, "Location");
            if (!location) return;
            // RFC 2616 では Location ヘッダの値は絶対 URI となっているが、
            // はてなブックマークでは絶対パスの相対 URI になっている。
            // どちらでも大丈夫なようにいったん URI を解決する。
            let path = newURI(location, null, channel.URI).path;
            // 再度タグ入力を求められる
            // (/user_id/tag?tag=... へ飛ばされる) なら失敗とみなす。
            if (/^\/[\w-]+\/tag\?/.test(path)) return;
            break;

        default:
            return;
        }
        let data = this._getPostData(channel);
        if (data.newtag)
            Model.Tag.rename(data.tag, data.newtag);
        else
            Model.Tag.deleteByName(data.tag);
    },

    _getResponseHeader: function HW__getResponseHeader(channel, header) {
        try {
            return channel.getResponseHeader(header);
        } catch (ex) {
            return null;
        }
    },

    _getPostData: function HW__getPostData(channel) {
        let rawStream = channel.uploadStream;
        if (!(rawStream instanceof Ci.nsISeekableStream)) return null;
        rawStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
        let stream = Cc["@mozilla.org/scriptableinputstream;1"].
                     createInstance(Ci.nsIScriptableInputStream);
        stream.init(rawStream);
        let body = stream.read(stream.available());
        if (rawStream instanceof Ci.nsIMIMEInputStream)
            body = body.replace(/^(?:.+\r\n)+\r\n/, "");
        return this._toMap(body);
    },

    _toMap: function HW__toMap(string) {
        let result = {};
        string.replace(/\+/g, "%20").split("&").forEach(function (pair) {
            let [key, value] = pair.split("=");
            try {
                value = decodeURIComponent(value);
            } catch (ex) {}
            result[key] = value;
        });
        return result;
    },

    EDIT_BOOKMARK_PATTERN:    /^\/(?:bookmarklet\.edit|[\w-]+\/add\.edit)\b/,
    DELETE_BOOKMARKS_PATTERN: /^\/[\w-]+\/api\.delete_bookmark\b/,
    EDIT_TAG_PATTERN:         /^\/[\w-]+\/tag\.(?:edit|delete)\b/,

    observe: function HW_observe(subject, topic, data) {
        if (!(subject instanceof Ci.nsIHttpChannel) ||
            !(subject instanceof Ci.nsIUploadChannel) ||
            !(subject.URI.host in this.targetHosts))
            return;
        let path = subject.URI.path;
        if (this.EDIT_BOOKMARK_PATTERN.test(path)) {
            this.onEditBookmark(subject);
        } else if (this.DELETE_BOOKMARKS_PATTERN.test(path)) {
            this.onDeleteBookmarks(subject);
        } else if (this.EDIT_TAG_PATTERN.test(path)) {
            this.onEditTag(subject);
        }
    },

    startObserving: function HW_startObserving() {
        ObserverService.addObserver(this, "http-on-examine-response", false);
        ObserverService.addObserver(this.quitObserver, "quit-application", false);
    },

    stopObserving: function HW_stopObserving() {
        p('stop HttpWatcher observing');
        ObserverService.removeObserver(this, "http-on-examine-response");
        ObserverService.removeObserver(this.quitObserver, "quit-application");
    },

    quitObserver: {
        observe: function HW_QO_observe(subject, topic, data) {
            HttpWatcher.stopObserving();
        }
    }
};

if (!shared.has("HttpWatcher")) {
    HttpWatcher.startObserving();
    shared.set("HttpWatcher", HttpWatcher);
}
<commit_msg>ブックマーク同期失敗時、再試行までの遅延時間を2秒から7秒へ延長。<commit_after>const EXPORT = ["HttpWatcher"];

var HttpWatcher = shared.get("HttpWatcher") || {
    // 監視するホスト
    targetHostsArray: [
        B_HOST,
        //"bbeta.hatena.ne.jp",
        //"localhost",
    ],

    get targetHosts HW_get_targetHosts() {
        let hosts = this.targetHostsArray.reduce(function (hosts, host) {
            hosts[host] = true;
            return hosts;
        }, new DictionaryObject());
        delete this.targetHosts;
        return this.targetHosts = hosts;
    },

    onEditBookmark: function HW_onEditBookmark(channel) {
        let url = this._getResponseHeader(channel, "X-Bookmark-URL");
        if (!url) return;
        let data = this._getPostData(channel);
        if (!data) return;

        // ブックマーク成功したら、sync する
        // これにより、リモートとのデータの同期がとれる
        // XXX: Sync に依存してしまう
        let listener = Sync.createListener("complete", function onSync() {
            p('Sync completed');
            listener.unlisten();
            HTTPCache.entry.cache.clear(data.url);
            if (!Model.Bookmark.findByUrl(data.url).length) {
                p(data.url + ' is not registered.  Retry sync.');
                // 同期が間に合わなかったら少し待ってもう一度だけ同期する。
                setTimeout(method(Sync, 'sync'), 7000);
            }
        }, null, 0, false);
        Sync.sync();

        let bookmark = Model.Bookmark.findByUrl(data.url)[0];
        if (bookmark) {
            // すでに存在するブックマークは Sync で
            // 同期できないので、ここで DB を更新しておく。
            if (data.title)
                bookmark.title = data.title;
            bookmark.comment = data.comment;
            bookmark.save();
            EventService.dispatch('BookmarksUpdated');
        }
    },

    onDeleteBookmarks: function HW_onDeleteBookmarks(channel) {
        if (channel.responseStatus !== 200) return;
        let data = this._getPostData(channel);
        let url = this._getResponseHeader(channel, "X-Bookmark-URL") || data.url;
        let urls = url ? [url] : data.urllist ? data.urllist.split("|") : null;
        if (!urls) return;
        Model.Bookmark.deleteByURLs(urls);
    },

    onEditTag: function HW_onEditTag(channel) {
        switch (channel.responseStatus) {
        case 200:
            // JSON API の成功
            break;

        // タグ編集ページ (/user_id/tag?tag=...) から編集した場合、
        // レスポンスはタグページまたはホームページへのリダイレクトになる。
        case 302:
            let location = this._getResponseHeader(channel, "Location");
            if (!location) return;
            // RFC 2616 では Location ヘッダの値は絶対 URI となっているが、
            // はてなブックマークでは絶対パスの相対 URI になっている。
            // どちらでも大丈夫なようにいったん URI を解決する。
            let path = newURI(location, null, channel.URI).path;
            // 再度タグ入力を求められる
            // (/user_id/tag?tag=... へ飛ばされる) なら失敗とみなす。
            if (/^\/[\w-]+\/tag\?/.test(path)) return;
            break;

        default:
            return;
        }
        let data = this._getPostData(channel);
        if (data.newtag)
            Model.Tag.rename(data.tag, data.newtag);
        else
            Model.Tag.deleteByName(data.tag);
    },

    _getResponseHeader: function HW__getResponseHeader(channel, header) {
        try {
            return channel.getResponseHeader(header);
        } catch (ex) {
            return null;
        }
    },

    _getPostData: function HW__getPostData(channel) {
        let rawStream = channel.uploadStream;
        if (!(rawStream instanceof Ci.nsISeekableStream)) return null;
        rawStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
        let stream = Cc["@mozilla.org/scriptableinputstream;1"].
                     createInstance(Ci.nsIScriptableInputStream);
        stream.init(rawStream);
        let body = stream.read(stream.available());
        if (rawStream instanceof Ci.nsIMIMEInputStream)
            body = body.replace(/^(?:.+\r\n)+\r\n/, "");
        return this._toMap(body);
    },

    _toMap: function HW__toMap(string) {
        let result = {};
        string.replace(/\+/g, "%20").split("&").forEach(function (pair) {
            let [key, value] = pair.split("=");
            try {
                value = decodeURIComponent(value);
            } catch (ex) {}
            result[key] = value;
        });
        return result;
    },

    EDIT_BOOKMARK_PATTERN:    /^\/(?:bookmarklet\.edit|[\w-]+\/add\.edit)\b/,
    DELETE_BOOKMARKS_PATTERN: /^\/[\w-]+\/api\.delete_bookmark\b/,
    EDIT_TAG_PATTERN:         /^\/[\w-]+\/tag\.(?:edit|delete)\b/,

    observe: function HW_observe(subject, topic, data) {
        if (!(subject instanceof Ci.nsIHttpChannel) ||
            !(subject instanceof Ci.nsIUploadChannel) ||
            !(subject.URI.host in this.targetHosts))
            return;
        let path = subject.URI.path;
        if (this.EDIT_BOOKMARK_PATTERN.test(path)) {
            this.onEditBookmark(subject);
        } else if (this.DELETE_BOOKMARKS_PATTERN.test(path)) {
            this.onDeleteBookmarks(subject);
        } else if (this.EDIT_TAG_PATTERN.test(path)) {
            this.onEditTag(subject);
        }
    },

    startObserving: function HW_startObserving() {
        ObserverService.addObserver(this, "http-on-examine-response", false);
        ObserverService.addObserver(this.quitObserver, "quit-application", false);
    },

    stopObserving: function HW_stopObserving() {
        p('stop HttpWatcher observing');
        ObserverService.removeObserver(this, "http-on-examine-response");
        ObserverService.removeObserver(this.quitObserver, "quit-application");
    },

    quitObserver: {
        observe: function HW_QO_observe(subject, topic, data) {
            HttpWatcher.stopObserving();
        }
    }
};

if (!shared.has("HttpWatcher")) {
    HttpWatcher.startObserving();
    shared.set("HttpWatcher", HttpWatcher);
}
<|endoftext|>


<commit_before>(function() {
'use strict';

/**
 * @description
 * The slideBoxCtrol lets you quickly create a multi-page
 * container where each page can be swiped or dragged between
 */

angular.module('ionic.ui.slideBox', [])

/**
 * The internal controller for the slide box controller.
 */

.directive('slideBox', ['$timeout', '$compile', function($timeout, $compile) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      doesContinue: '@',
      slideInterval: '@',
      showPager: '@',
      disableScroll: '@',
      onSlideChanged: '&'
    },
    controller: ['$scope', '$element', function($scope, $element) {
      var _this = this;

      var continuous = $scope.$eval($scope.doesContinue) === true;
      var slideInterval = continuous ? $scope.$eval($scope.slideInterval) || 4000 : 0;

      var slider = new ionic.views.Slider({
        el: $element[0],
        auto: slideInterval,
        disableScroll: ($scope.$eval($scope.disableScroll) === true) || false,
        continuous: continuous,
        slidesChanged: function() {
          $scope.currentSlide = slider.getPos();

          // Try to trigger a digest
          $timeout(function() {});
        },
        callback: function(slideIndex) {
          $scope.currentSlide = slideIndex;
          $scope.onSlideChanged({index:$scope.currentSlide});
          $scope.$parent.$broadcast('slideBox.slideChanged', slideIndex);

          // Try to trigger a digest
          $timeout(function() {});
        }
      });

      $scope.$on('slideBox.nextSlide', function() {
        slider.next();
      });

      $scope.$on('slideBox.prevSlide', function() {
        slider.prev();
      });

      $scope.$on('slideBox.setSlide', function(e, index) {
        slider.slide(index);
      });

      $scope.$parent.slideBox = slider;

      this.getNumSlides = function() {
        return slider.getNumSlides();
      };

      $timeout(function() {
        slider.load();
      });
    }],
    template: '
\

\

\

',

    link: function($scope, $element, $attr, slideBoxCtrl) {
      // If the pager should show, append it to the slide box
      if($scope.$eval($scope.showPager) !== false) {
        var childScope = $scope.$new();
        var pager = angular.element('<pager></pager>');
        $element.append(pager);
        $compile(pager)(childScope);
      }
    }
  };
}])

.directive('slide', function() {
  return {
    restrict: 'E',
    require: '^slideBox',
    compile: function(element, attr) {
      element.addClass('slider-slide');
      return function($scope, $element, $attr) {};
    },
  };
})

.directive('pager', function() {
  return {
    restrict: 'E',
    replace: true,
    require: '^slideBox',
    template: '

',
    link: function($scope, $element, $attr, slideBox) {
      var selectPage = function(index) {
        var children = $element[0].children;
        var length = children.length;
        for(var i = 0; i < length; i++) {
          if(i == index) {
            children[i].classList.add('active');
          } else {
            children[i].classList.remove('active');
          }
        }
      };

      $scope.numSlides = function() {
        return new Array(slideBox.getNumSlides());
      };

      $scope.$watch('currentSlide', function(v) {
        selectPage(v);
      });
    }
  };

});

})();
<commit_msg>Add binding to active slide, setting on change and watch on scope<commit_after>(function() {
'use strict';

/**
 * @description
 * The slideBoxCtrol lets you quickly create a multi-page
 * container where each page can be swiped or dragged between
 */

angular.module('ionic.ui.slideBox', [])

/**
 * The internal controller for the slide box controller.
 */

.directive('slideBox', ['$timeout', '$compile', function($timeout, $compile) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      doesContinue: '@',
      slideInterval: '@',
      showPager: '@',
      disableScroll: '@',
      onSlideChanged: '&',
      activeSlide: '='
    },
    controller: ['$scope', '$element', function($scope, $element) {
      var _this = this;

      var continuous = $scope.$eval($scope.doesContinue) === true;
      var slideInterval = continuous ? $scope.$eval($scope.slideInterval) || 4000 : 0;

      var slider = new ionic.views.Slider({
        el: $element[0],
        auto: slideInterval,
        disableScroll: ($scope.$eval($scope.disableScroll) === true) || false,
        continuous: continuous,
        slidesChanged: function() {
          $scope.currentSlide = slider.getPos();

          // Try to trigger a digest
          $timeout(function() {});
        },
        callback: function(slideIndex) {
          $scope.currentSlide = slideIndex;
          $scope.onSlideChanged({index:$scope.currentSlide});
          $scope.$parent.$broadcast('slideBox.slideChanged', slideIndex);
          $scope.activeSlide = slideIndex;
          // Try to trigger a digest
          $timeout(function() {});
        }
      });

      $scope.$watch('activeSlide', function(nv) {
        if(angular.isDefined(nv)){
          slider.slide(nv);
        }
      });

      $scope.$on('slideBox.nextSlide', function() {
        slider.next();
      });

      $scope.$on('slideBox.prevSlide', function() {
        slider.prev();
      });

      $scope.$on('slideBox.setSlide', function(e, index) {
        slider.slide(index);
      });

      $scope.$parent.slideBox = slider;

      this.getNumSlides = function() {
        return slider.getNumSlides();
      };

      $timeout(function() {
        slider.load();
      });
    }],
    template: '
\

\

\

',

    link: function($scope, $element, $attr, slideBoxCtrl) {
      // If the pager should show, append it to the slide box
      if($scope.$eval($scope.showPager) !== false) {
        var childScope = $scope.$new();
        var pager = angular.element('<pager></pager>');
        $element.append(pager);
        $compile(pager)(childScope);
      }
    }
  };
}])

.directive('slide', function() {
  return {
    restrict: 'E',
    require: '^slideBox',
    compile: function(element, attr) {
      element.addClass('slider-slide');
      return function($scope, $element, $attr) {};
    },
  };
})

.directive('pager', function() {
  return {
    restrict: 'E',
    replace: true,
    require: '^slideBox',
    template: '

',
    link: function($scope, $element, $attr, slideBox) {
      var selectPage = function(index) {
        var children = $element[0].children;
        var length = children.length;
        for(var i = 0; i < length; i++) {
          if(i == index) {
            children[i].classList.add('active');
          } else {
            children[i].classList.remove('active');
          }
        }
      };

      $scope.numSlides = function() {
        return new Array(slideBox.getNumSlides());
      };

      $scope.$watch('currentSlide', function(v) {
        selectPage(v);
      });
    }
  };

});

})();
<|endoftext|>


<commit_before>angular.module('ngIntlTelInput')
  .directive('ngIntlTelInput', ['ngIntlTelInput', '$log',
    function (ngIntlTelInput, $log) {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attr, ctrl) {
          // Warning for bad directive usage.
          if (attr.type !== 'text' || elm[0].tagName !== 'INPUT') {
            $log.warn('ng-intl-tel-input can only be applied to a *text* input');
            return;
          }
          // Override default country.
          if (attr.defaultCountry) {
            ngIntlTelInput.set({defaultCountry: attr.defaultCountry});
          }
          // Initialize.
          ngIntlTelInput.init(elm);
          // Validation.
          ctrl.$validators.ngIntlTelInput = function (value) {
            // if phone number is deleted / empty do not run phone number validation
            if (value || elm[0].value.length > 1) {
                return elm.intlTelInput("isValidNumber");
            } else {
                return true;
            }
          };
          // Set model value to valid, formatted version.
          ctrl.$parsers.push(function (value) {
            return elm.intlTelInput('getNumber').replace(/[^\d]/, '');
          });
          // Set input value to model value and trigger evaluation.
          ctrl.$formatters.push(function (value) {
            if (value) {
              value = value.charAt(0) === '+' || '+' + value;
              elm.intlTelInput('setNumber', value);
            }
            return value;
          });
        }
      };
    }]);
<commit_msg>change validation length<commit_after>angular.module('ngIntlTelInput')
  .directive('ngIntlTelInput', ['ngIntlTelInput', '$log',
    function (ngIntlTelInput, $log) {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attr, ctrl) {
          // Warning for bad directive usage.
          if (attr.type !== 'text' || elm[0].tagName !== 'INPUT') {
            $log.warn('ng-intl-tel-input can only be applied to a *text* input');
            return;
          }
          // Override default country.
          if (attr.defaultCountry) {
            ngIntlTelInput.set({defaultCountry: attr.defaultCountry});
          }
          // Initialize.
          ngIntlTelInput.init(elm);
          // Validation.
          ctrl.$validators.ngIntlTelInput = function (value) {
            // if phone number is deleted / empty do not run phone number validation
            if (value || elm[0].value.length > 0) {
                return elm.intlTelInput("isValidNumber");
            } else {
                return true;
            }
          };
          // Set model value to valid, formatted version.
          ctrl.$parsers.push(function (value) {
            return elm.intlTelInput('getNumber').replace(/[^\d]/, '');
          });
          // Set input value to model value and trigger evaluation.
          ctrl.$formatters.push(function (value) {
            if (value) {
              value = value.charAt(0) === '+' || '+' + value;
              elm.intlTelInput('setNumber', value);
            }
            return value;
          });
        }
      };
    }]);
<|endoftext|>


<commit_before>import Webiny from 'Webiny';

class SelectInput extends Webiny.Ui.FormComponent {

    constructor(props) {
        super(props);

        this.select2 = null;
        this.options = null;
        this.bindMethods('getConfig,getValue,triggerChange,getSelect2InputElement,itemRenderer');
    }

    componentDidMount() {
        super.componentDidMount();
        this.select2 = this.getSelect2InputElement().select2(this.getConfig(this.props));
        this.select2.on('select2:select', e => {
            this.triggerChange(e.target.value);
        });
        this.select2.on('select2:unselect', () => {
            this.triggerChange('');
        });
        this.select2.val(this.getValue()).trigger('change');
    }

    componentDidUpdate() {
        super.componentDidUpdate();
        const possibleValues = _.map(this.options, 'id');
        const value = this.getValue();
        const inPossibleValues = possibleValues.indexOf(value) > -1;

        if (!this.options || !_.isEqual(this.props.options, this.options)) {
            this.select2.empty();
            this.getSelect2InputElement().select2(this.getConfig(this.props));
            setTimeout(() => {
                this.select2.val(this.getValue()).trigger('change');
            }, 100);
        }

        $(ReactDOM.findDOMNode(this)).find('select').prop('disabled', !!this.isDisabled());

        if (value !== null && !inPossibleValues && possibleValues.length > 0) {
            this.triggerChange(null);
            return;
        }

        if (value !== null && inPossibleValues) {
            this.select2.val(value).trigger('change');
            return;
        }

        this.select2.val('').trigger('change');
    }

    getSelect2InputElement() {
        return $(ReactDOM.findDOMNode(this)).find('select');
    }

    getValue() {
        const value = this.props.valueLink ? this.props.valueLink.value : this.props.selectedValue;
        if (value === null || value === undefined) {
            return value;
        }

        return _.isObject(value) ? value.id : '' + value;
    }

    triggerChange(value) {
        if (this.props.useDataAsValue && value) {
            const selectedOption = _.find(this.options, {id: value});
            if (!selectedOption.data) {
                console.warn('Warning: attempting to use item data but data is not present in option items!');
            } else {
                value = selectedOption.data;
            }
        }
        if (this.props.valueLink) {
            this.props.valueLink.requestChange(value);
        }
        this.props.onChange(value);
    }

    getOptionText(text) {
        if (!text) {
            return '';
        }

        if (text.startsWith('<')) {
            return $(text);
        }

        return text || '';
    }

    /**
     * This will be triggered twice due to a bug in Select2 (https://github.com/select2/select2/pull/4306)
     * @param item
     * @param type optionRenderer || selectedRenderer
     * @returns {*}
     */
    itemRenderer(item, type) {
        let text = item.text;
        if (_.isFunction(this.props[type]) && item.data) {
            text = this.props[type].call(this, item.data || {});
        }

        if (!_.isString(text)) {
            text = ReactDOMServer.renderToStaticMarkup(text);
        }

        return this.getOptionText(text);
    }

    getConfig(props) {
        const config = {
            disabled: this.isDisabled(props),
            minimumResultsForSearch: props.minimumResultsForSearch,
            placeholder: props.placeholder,
            allowClear: props.allowClear,
            templateResult: item => this.itemRenderer(item, 'optionRenderer'),
            templateSelection: item => this.itemRenderer(item, 'selectedRenderer')
        };

        if (!this.options || !_.isEqual(props.options, this.options)) {
            // Prepare options
            const options = [];
            _.each(props.options, option => {
                if (React.isValidElement(option.text)) {
                    option.text = ReactDOMServer.renderToStaticMarkup(option.text);
                }
                options.push(option);
            });
            config['data'] = this.options = options;
        }

        return config;
    }
}

SelectInput.defaultProps = {
    allowClear: false,
    placeholder: null,
    onChange: _.noop,
    selectedValue: '',
    minimumResultsForSearch: 15,
    useDataAsValue: false,
    renderer() {
        const cssConfig = {
            'form-group': true,
            'error': this.state.isValid === false,
            'success': this.state.isValid === true
        };

        let label = null;
        if (this.props.label) {
            label = {this.props.label};
        }

        let validationMessage = null;

        if (this.state.isValid === false) {
            validationMessage = ({this.state.validationMessage});
        }

        return (

                {label}
                <select style={{'width': '100%'}}/>
                {validationMessage}

        );
    }
};

export default SelectInput;<commit_msg>Removed double onChange callback<commit_after>import Webiny from 'Webiny';

class SelectInput extends Webiny.Ui.FormComponent {

    constructor(props) {
        super(props);

        this.select2 = null;
        this.options = null;
        this.bindMethods('getConfig,getValue,triggerChange,getSelect2InputElement,itemRenderer');
    }

    componentDidMount() {
        super.componentDidMount();
        this.select2 = this.getSelect2InputElement().select2(this.getConfig(this.props));
        this.select2.on('select2:select', e => {
            this.triggerChange(e.target.value);
        });
        this.select2.on('select2:unselect', () => {
            this.triggerChange('');
        });
        this.select2.val(this.getValue()).trigger('change');
    }

    componentDidUpdate() {
        super.componentDidUpdate();
        const possibleValues = _.map(this.options, 'id');
        const value = this.getValue();
        const inPossibleValues = possibleValues.indexOf(value) > -1;

        if (!this.options || !_.isEqual(this.props.options, this.options)) {
            this.select2.empty();
            this.getSelect2InputElement().select2(this.getConfig(this.props));
            setTimeout(() => {
                this.select2.val(this.getValue()).trigger('change');
            }, 100);
        }

        $(ReactDOM.findDOMNode(this)).find('select').prop('disabled', !!this.isDisabled());

        if (value !== null && !inPossibleValues && possibleValues.length > 0) {
            this.triggerChange(null);
            return;
        }

        if (value !== null && inPossibleValues) {
            this.select2.val(value).trigger('change');
            return;
        }

        this.select2.val('').trigger('change');
    }

    getSelect2InputElement() {
        return $(ReactDOM.findDOMNode(this)).find('select');
    }

    getValue() {
        const value = this.props.valueLink ? this.props.valueLink.value : this.props.selectedValue;
        if (value === null || value === undefined) {
            return value;
        }

        return _.isObject(value) ? value.id : '' + value;
    }

    triggerChange(value) {
        if (this.props.useDataAsValue && value) {
            const selectedOption = _.find(this.options, {id: value});
            if (!selectedOption.data) {
                console.warn('Warning: attempting to use item data but data is not present in option items!');
            } else {
                value = selectedOption.data;
            }
        }
        if (this.props.valueLink) {
            this.props.valueLink.requestChange(value);
        }
    }

    getOptionText(text) {
        if (!text) {
            return '';
        }

        if (text.startsWith('<')) {
            return $(text);
        }

        return text || '';
    }

    /**
     * This will be triggered twice due to a bug in Select2 (https://github.com/select2/select2/pull/4306)
     * @param item
     * @param type optionRenderer || selectedRenderer
     * @returns {*}
     */
    itemRenderer(item, type) {
        let text = item.text;
        if (_.isFunction(this.props[type]) && item.data) {
            text = this.props[type].call(this, item.data || {});
        }

        if (!_.isString(text)) {
            text = ReactDOMServer.renderToStaticMarkup(text);
        }

        return this.getOptionText(text);
    }

    getConfig(props) {
        const config = {
            disabled: this.isDisabled(props),
            minimumResultsForSearch: props.minimumResultsForSearch,
            placeholder: props.placeholder,
            allowClear: props.allowClear,
            templateResult: item => this.itemRenderer(item, 'optionRenderer'),
            templateSelection: item => this.itemRenderer(item, 'selectedRenderer')
        };

        if (!this.options || !_.isEqual(props.options, this.options)) {
            // Prepare options
            const options = [];
            _.each(props.options, option => {
                if (React.isValidElement(option.text)) {
                    option.text = ReactDOMServer.renderToStaticMarkup(option.text);
                }
                options.push(option);
            });
            config['data'] = this.options = options;
        }

        return config;
    }
}

SelectInput.defaultProps = {
    allowClear: false,
    placeholder: null,
    onChange: _.noop,
    selectedValue: '',
    minimumResultsForSearch: 15,
    useDataAsValue: false,
    renderer() {
        const cssConfig = {
            'form-group': true,
            'error': this.state.isValid === false,
            'success': this.state.isValid === true
        };

        let label = null;
        if (this.props.label) {
            label = {this.props.label};
        }

        let validationMessage = null;

        if (this.state.isValid === false) {
            validationMessage = ({this.state.validationMessage});
        }

        return (

                {label}
                <select style={{'width': '100%'}}/>
                {validationMessage}

        );
    }
};

export default SelectInput;<|endoftext|>


<commit_before>/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

import {
  ViroSceneNavigator,
  ViroScene,
  ViroBox,
  ViroMaterials,
  ViroNode,
  ViroOrbitCamera,
  ViroCamera,
  ViroAmbientLight,
  ViroOmniLight,
  ViroSpotLight,
  ViroDirectionalLight,
  ViroImage,
  ViroVideo,
  Viro360Image,
  Viro360Video,
  ViroFlexView,
  ViroUtils,
  ViroText,
  ViroAnimations,
  ViroAnimatedComponent,
  ViroSurface,
  ViroSkyBox,
  ViroSphere,
  Viro3DObject,
} from 'react-viro';

var createReactClass = require('create-react-class');

let polarToCartesian = ViroUtils.polarToCartesian;
var UriImage = {uri:"https://lh3.googleusercontent.com/dB3Dvgf3VIglusoGJAfpNUAANhTXW8K9mvIsiIPkhJUAbAKGKJcEMPTf0mkSexzLM5o=w300"};
var ReleaseMenu = require("./ReleaseMenu.js");

var ViroBoxTest = createReactClass({

  getInitialState() {
    return {
      runAnimation1:false,
      runAnimation2:false,
      runAnimation3:false,
      runAnimation4:false,
      toggleWHL:1,
      usingMaterialArray:false,
      currentMaterials:["box1"],
    };
  },

  render: function() {
    return (
      <ViroScene>
      <ReleaseMenu sceneNavigator={this.props.sceneNavigator}/>
      <ViroOmniLight position={[0, 0, 0]} color="#ffffff" attenuationStartDistance={40} attenuationEndDistance={50}/>

      <ViroImage source={require('./res/poi_dot.png')} position={[-1, 0, 0]} transformBehaviors={["billboard"]} onClick={this._showNext} />

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation1} loop={true}>
         <ViroBox
            position={[-2, 2, -4]}
            scale={[1,1,1]}
            materials={this.state.currentMaterials}
            rotation={[0,0,0]}
            height={1*this.state.toggleWHL}
            width={1*this.state.toggleWHL}
            length={1*this.state.toggleWHL}
            opacity={.5}
            transformBehaviors={["billboard"]}
            onHover={this._startAnimation1}
          />
      </ViroAnimatedComponent>

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation2} loop={true}>
        <ViroBox
            position={[2, 2, -4]}
            scale={[1,1,1]}
            materials={this.state.currentMaterials}
            rotation={[0,45,0]}
            height={1*this.state.toggleWHL}
            width={1*this.state.toggleWHL}
            length={1*this.state.toggleWHL}
            transformBehaviors={["billboardX"]}
            onClick={this._startAnimation2}
        />
      </ViroAnimatedComponent>

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation3} loop={true}>
        <ViroBox
            position={[-3, -3, -4]}
            scale={[2,2,2]}
            materials={this.state.currentMaterials}
            rotation={[0,0,0]}
            height={1*this.state.toggleWHL}
            width={1*this.state.toggleWHL}
            length={1*this.state.toggleWHL}
            transformBehaviors={["billboardY"]}
            onScroll={this._startAnimation3}
        />
      </ViroAnimatedComponent>

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation4} loop={true}>
        <ViroBox
            position={[2, -1.3, -4]}
            scale={[1,1,1]}
            materials={this.state.currentMaterials}
            rotation={[0,0,0]}
            height={2*this.state.toggleWHL}
            width={4*this.state.toggleWHL}
            length={3*this.state.toggleWHL}
            transformBehaviors={["billboardZ"]}
            onSwipe={this._startAnimation4}
          />
      </ViroAnimatedComponent>

      <ViroImage source={require('./res/poi_dot.png')} position={[-1, 0, 0]} transformBehaviors={["billboard"]} onClick={this._showNext} />
      <ViroText fontSize={this.state.fontSize} style={styles.centeredText} position={[0,-4, -3]} width={2} height ={2} fontFamily={'Arial'}
          text={"Toggle WHL: " + this.state.toggleWHL} textLineBreakMode='justify' onClick={this._toggleWHL}/>

      <ViroText fontSize={this.state.fontSize} style={styles.centeredText} position={[0,-5, -3]} width={2} height ={2} fontFamily={'Arial'}
          text={"Toggle materials" } textLineBreakMode='justify' onClick={this._toggleMaterials}/>

     </ViroScene>
    );
  },

  _showNext() {
    this.props.sceneNavigator.replace({scene:require('./ViroSphereTest')});
  },

  _toggleMaterials() {
    var newMat = !this.state.usingMaterialArray ? ["box2", "box2", "box2", "box1", "box1", "box1"] : ["box1"];
    var newUsingMaterialArray = !this.state.usingMaterialArray;
    this.setState({
        currentMaterials: newMat,
        usingMaterialArray: newUsingMaterialArray,
    });
  },

  _toggleWHL(){
    var newWhlScale = this.state.toggleWHL + 0.1;
    if (newWhlScale > 1.3){
        newWhlScale = 1;
    }
    this.setState({
        toggleWHL: newWhlScale,
    });
  },

  _showPrevious() {
    this.props.sceneNavigator.pop();
  },

  _startAnimation1() {
    this.setState({
        runAnimation1:true,
      });
  },

  _startAnimation2() {
    this.setState({
        runAnimation2:true,
      });
  },

  _startAnimation3() {
    this.setState({
        runAnimation3:true,
      });
  },

  _startAnimation4() {
    this.setState({
        runAnimation4:true,
      });
  },
});

ViroAnimations.registerAnimations({
  loopRotate:{properties:{rotateY:"+=10"}, duration:250},
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  elementText: {
    fontFamily: 'HelveticaNeue-Medium',
    fontSize: 30,
    color: '#ffffff',
    textAlign: 'center',
  },
  centeredText: {
    fontSize: 20,
    fontFamily: 'Arial',
    color: '#ffffff',
  },
});

ViroMaterials.createMaterials({
   box1: {
     bloomThreshold : 1.0,
     shininess : 2.0,
     lightingModel: "Blinn",
     diffuseTexture: require('./res/earth.png'),
   },
   box2: {
     shininess : 2.0,
     lightingModel: "Blinn",
     diffuseColor: "#0000FF",
   },
});

module.exports = ViroBoxTest;
<commit_msg>Fix Viro Box Tests Crash<commit_after>/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

import {
  ViroSceneNavigator,
  ViroScene,
  ViroBox,
  ViroMaterials,
  ViroNode,
  ViroOrbitCamera,
  ViroCamera,
  ViroAmbientLight,
  ViroOmniLight,
  ViroSpotLight,
  ViroDirectionalLight,
  ViroImage,
  ViroVideo,
  Viro360Image,
  Viro360Video,
  ViroFlexView,
  ViroUtils,
  ViroText,
  ViroAnimations,
  ViroAnimatedComponent,
  ViroSurface,
  ViroSkyBox,
  ViroSphere,
  Viro3DObject,
} from 'react-viro';

var createReactClass = require('create-react-class');

let polarToCartesian = ViroUtils.polarToCartesian;
var UriImage = {uri:"https://lh3.googleusercontent.com/dB3Dvgf3VIglusoGJAfpNUAANhTXW8K9mvIsiIPkhJUAbAKGKJcEMPTf0mkSexzLM5o=w300"};
var ReleaseMenu = require("./ReleaseMenu.js");

var ViroBoxTest = createReactClass({

  getInitialState() {
    return {
      runAnimation1:false,
      runAnimation2:false,
      runAnimation3:false,
      runAnimation4:false,
      toggleWHL:1,
      usingMaterialArray:false,
      currentMaterials:["box1"],
    };
  },

  render: function() {
    return (
      <ViroScene>
      <ReleaseMenu sceneNavigator={this.props.sceneNavigator}/>
      <ViroOmniLight position={[0, 0, 0]} color="#ffffff" attenuationStartDistance={40} attenuationEndDistance={50}/>

      <ViroImage source={require('./res/poi_dot.png')} position={[-1, 0, 0]} transformBehaviors={["billboard"]} onClick={this._showNext} />

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation1} loop={true}>
         <ViroBox
            position={[-2, 2, -4]}
            scale={[1,1,1]}
            materials={this.state.currentMaterials}
            rotation={[0,0,0]}
            height={1*this.state.toggleWHL}
            width={1*this.state.toggleWHL}
            length={1*this.state.toggleWHL}
            opacity={.5}
            transformBehaviors={["billboard"]}
            onHover={this._startAnimation1}
          />
      </ViroAnimatedComponent>

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation2} loop={true}>
        <ViroBox
            position={[2, 2, -4]}
            scale={[1,1,1]}
            materials={this.state.currentMaterials}
            rotation={[0,45,0]}
            height={1*this.state.toggleWHL}
            width={1*this.state.toggleWHL}
            length={1*this.state.toggleWHL}
            transformBehaviors={["billboardX"]}
            onClick={this._startAnimation2}
        />
      </ViroAnimatedComponent>

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation3} loop={true}>
        <ViroBox
            position={[-3, -3, -4]}
            scale={[2,2,2]}
            materials={this.state.currentMaterials}
            rotation={[0,0,0]}
            height={1*this.state.toggleWHL}
            width={1*this.state.toggleWHL}
            length={1*this.state.toggleWHL}
            transformBehaviors={["billboardY"]}
            onScroll={this._startAnimation3}
        />
      </ViroAnimatedComponent>

      <ViroAnimatedComponent animation="loopRotate" run={this.state.runAnimation4} loop={true}>
        <ViroBox
            position={[2, -1.3, -4]}
            scale={[1,1,1]}
            materials={this.state.currentMaterials}
            rotation={[0,0,0]}
            height={2*this.state.toggleWHL}
            width={4*this.state.toggleWHL}
            length={3*this.state.toggleWHL}
            transformBehaviors={["billboard"]}
            onSwipe={this._startAnimation4}
          />
      </ViroAnimatedComponent>

      <ViroImage source={require('./res/poi_dot.png')} position={[-1, 0, 0]} transformBehaviors={["billboard"]} onClick={this._showNext} />
      <ViroText fontSize={this.state.fontSize} style={styles.centeredText} position={[0,-4, -3]} width={2} height ={2} fontFamily={'Arial'}
          text={"Toggle WHL: " + this.state.toggleWHL} textLineBreakMode='justify' onClick={this._toggleWHL}/>

      <ViroText fontSize={this.state.fontSize} style={styles.centeredText} position={[0,-5, -3]} width={2} height ={2} fontFamily={'Arial'}
          text={"Toggle materials" } textLineBreakMode='justify' onClick={this._toggleMaterials}/>

     </ViroScene>
    );
  },

  _showNext() {
    this.props.sceneNavigator.replace({scene:require('./ViroSphereTest')});
  },

  _toggleMaterials() {
    var newMat = !this.state.usingMaterialArray ? ["box2", "box2", "box2", "box1", "box1", "box1"] : ["box1"];
    var newUsingMaterialArray = !this.state.usingMaterialArray;
    this.setState({
        currentMaterials: newMat,
        usingMaterialArray: newUsingMaterialArray,
    });
  },

  _toggleWHL(){
    var newWhlScale = this.state.toggleWHL + 0.1;
    if (newWhlScale > 1.3){
        newWhlScale = 1;
    }
    this.setState({
        toggleWHL: newWhlScale,
    });
  },

  _showPrevious() {
    this.props.sceneNavigator.pop();
  },

  _startAnimation1() {
    this.setState({
        runAnimation1:true,
      });
  },

  _startAnimation2() {
    this.setState({
        runAnimation2:true,
      });
  },

  _startAnimation3() {
    this.setState({
        runAnimation3:true,
      });
  },

  _startAnimation4() {
    this.setState({
        runAnimation4:true,
      });
  },
});

ViroAnimations.registerAnimations({
  loopRotate:{properties:{rotateY:"+=10"}, duration:250},
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  elementText: {
    fontFamily: 'HelveticaNeue-Medium',
    fontSize: 30,
    color: '#ffffff',
    textAlign: 'center',
  },
  centeredText: {
    fontSize: 20,
    fontFamily: 'Arial',
    color: '#ffffff',
  },
});

ViroMaterials.createMaterials({
   box1: {
     bloomThreshold : 1.0,
     shininess : 2.0,
     lightingModel: "Blinn",
     diffuseTexture: require('./res/earth.png'),
   },
   box2: {
     shininess : 2.0,
     lightingModel: "Blinn",
     diffuseColor: "#0000FF",
   },
});

module.exports = ViroBoxTest;
<|endoftext|>


<commit_before>
var isOperator = function(input) {
  return ['/', '*', '-', '+'].indexOf(input) >= 0;
};

var isNumber = function(input) {
  return parseFloat(input) == input;
};

$(document).ready(function() {

  var entry = '0', expression = [];

  var updateScreen = function() {
    $('div#entry').text(entry);
    if (expression.length === 0) {
      $('div#expression').text('0');
    } else {
      $('div#expression').text(expression.join(''));
    }
  };

  var clearAll = function() {
    entry = '0';
    expression = [];
  };

  var clearEntry = function() {
    entry = '0';
    expression.pop();
  };

  var calculate = function() {
    entry = eval($('div#expression').text()).toString();
    expression.push(entry);
  };

  var chars = [
    'AC', 'CE', '/', '*',
    '7', '8', '9', '-',
    '4', '5', '6', '+',
    '3', '2', '1', '=',
    '0', '.'
  ];

  chars.forEach(function(char) {
    $('div#key').append($('<button>').text(char));
  });

  $('button:lt(2)').addClass('red');
  $('button:gt(1)').addClass('black');

  $('button:eq(16)').addClass('width2x');
  $('button:eq(15)').addClass('height2x');

  updateScreen();

  $('div#key button').click(function() {
    var input = $(this).text();

    if ('AC' === input) {
      clearAll();
    } else if ('CE' === input) {
      clearEntry();
    } else {
      /* Operator or Calculator (=) */
      if (isOperator(input) || '=' === input) {
        // if the first input is operator, set entry as default
        if (0 === expression.length) expression.push(entry);
        // auto correct (cancel last time operator)
        if (isOperator(expression[expression.length-1])) expression.pop();
        entry = input;
      }
      /* Operand */
      else {
        // new round
        if (0 === expression.length) entry = '0';
        // clear last input (entry)
        if (isNumber(entry)) expression.pop();

        if ('.' === input) {
          if (isNumber(entry)) {
            if (entry.search(/\./) === -1) {
              entry += input;
            } // ignore if contains '.'
          } else { // default entry = 0
            entry = '0.';
          }
        }

        if (isNumber(input)) {
          if (isNumber(entry) && '0' !== entry) {
            entry += input;
          } else {
            entry = input;
          }
        }
      }

      expression.push(entry);

      if ('=' === input) calculate();
    }

    console.log(expression);

    updateScreen();
    if ('=' === input) expression = [];
  });
});
<commit_msg>bugfix: '=' in eval()<commit_after>
var isOperator = function(input) {
  return ['/', '*', '-', '+'].indexOf(input) >= 0;
};

var isNumber = function(input) {
  return parseFloat(input) == input;
};

$(document).ready(function() {

  var entry = '0', expression = [];

  var buildExpression = function() {
    if (expression.length === 0) return '0';
    return expression.join('');
  };

  var updateScreen = function() {
    $('div#entry').text(entry);
    $('div#expression').text(buildExpression());
  };

  var clearAll = function() {
    entry = '0';
    expression = [];
  };

  var clearEntry = function() {
    entry = '0';
    expression.pop();
  };

  var calculate = function() {
    console.log(buildExpression());
    entry = eval(buildExpression()).toString();
    expression.push('=');
    expression.push(entry);
  };

  var chars = [
    'AC', 'CE', '/', '*',
    '7', '8', '9', '-',
    '4', '5', '6', '+',
    '3', '2', '1', '=',
    '0', '.'
  ];

  chars.forEach(function(char) {
    $('div#key').append($('<button>').text(char));
  });

  $('button:lt(2)').addClass('red');
  $('button:gt(1)').addClass('black');

  $('button:eq(16)').addClass('width2x');
  $('button:eq(15)').addClass('height2x');

  updateScreen();

  $('div#key button').click(function() {
    var input = $(this).text();

    if ('AC' === input) {
      clearAll();
    } else if ('CE' === input) {
      clearEntry();
    } else {
      /* Operator or Calculator (=) */
      if (isOperator(input) || '=' === input) {
        // if the first input is operator, set entry as default
        if (0 === expression.length) expression.push(entry);
        // auto correct (cancel last time operator)
        if (isOperator(expression[expression.length-1])) expression.pop();
        entry = input;
      }
      /* Operand */
      else {
        // new round
        if (0 === expression.length) entry = '0';
        // clear last input (entry)
        if (isNumber(entry)) expression.pop();

        if ('.' === input) {
          if (isNumber(entry)) {
            if (entry.search(/\./) === -1) {
              entry += input;
            } // ignore if contains '.'
          } else { // default entry = 0
            entry = '0.';
          }
        }

        if (isNumber(input)) {
          if (isNumber(entry) && '0' !== entry) {
            entry += input;
          } else {
            entry = input;
          }
        }
      }

      if ('=' === input) calculate();
      else expression.push(entry);
    }

    console.log(expression);

    updateScreen();
    if ('=' === input) expression = [];
  });
});
<|endoftext|>


<commit_before>"use strict";
var AddController = function($http, $log, $scope, navigation, resourceCache) {
	$scope.hike = {};
	$scope.hike.location = {};

	$scope.add = function() {
		$http({method: "POST", url: "/api/v1/hikes", data: $scope.hike}).
			success(function(data, status, headers, config) {
				var id = "";
				if (status === 202) {
					// The logic for converting name into id needs to stay in sync with the same function on the server
					id = $scope.hike.name.toLowerCase().replace(/#/g, "").split(" ").join("-");
					$scope.hike.string_id = id;
					// Keep this temporary version in the user's session cache, in case they decide to make other changes.
					resourceCache.put("/api/v1/hikes/" + $scope.hike.string_id, jQuery.extend(true, {}, $scope.hike));
				} else if (status === 200) {
					id = data.string_id;
				}
				navigation.toEntryEdit(id);
				$scope.hike = {};
				$scope.hike.location = {};
			}).
			error(function(data, status, headers, config) {
				$log.error(data, status, headers, config);
			}
		);
	};
	$scope.htmlReady();
};

AddController.$inject = ["$http", "$log", "$scope", "navigation", "resourceCache"];<commit_msg>Cache POST response to obviate the need for a subsequent get.<commit_after>"use strict";
var AddController = function($http, $log, $scope, navigation, resourceCache) {
	$scope.hike = {};
	$scope.hike.location = {};

	$scope.add = function() {
		$http({method: "POST", url: "/api/v1/hikes", data: $scope.hike}).
			success(function(data, status, headers, config) {
				var id = "";
				if (status === 202) {
					// The logic for converting name into id needs to stay in sync with the same function on the server
					id = $scope.hike.name.toLowerCase().replace(/#/g, "").split(" ").join("-");
					$scope.hike.string_id = id;
				} else if (status === 200) {
					id = data.string_id;
					$scope.hike = data;
				}
				// Keep this hike in the user's session cache (even in the case when the add is under review, otherwise
				// the redirect to the entry page would fail)
				resourceCache.put("/api/v1/hikes/" + $scope.hike.string_id, jQuery.extend(true, {}, $scope.hike));
				navigation.toEntryEdit(id);
				$scope.hike = {};
				$scope.hike.location = {};
			}).
			error(function(data, status, headers, config) {
				$log.error(data, status, headers, config);
			}
		);
	};
	$scope.htmlReady();
};

AddController.$inject = ["$http", "$log", "$scope", "navigation", "resourceCache"];<|endoftext|>