<commit_before>/**
 *  BigNumber
 *
 *  A wrapper around the BN.js object. In the future we can swap out
 *  the underlying BN.js library for something smaller.
 */

var BN = require('bn.js');

var defineProperty = require('./properties').defineProperty;
var convert = require('./convert');
var throwError = require('./throw-error');

function BigNumber(value) {
    if (!(this instanceof BigNumber)) { throw new Error('missing new'); }

    if (convert.isHexString(value)) {
        if (value == '0x') { value = '0x0'; }
        value = new BN(value.substring(2), 16);

    } else if (typeof(value) === 'string' && value.match(/^-?[0-9]*$/)) {
        if (value == '') { value = '0'; }
        value = new BN(value);

    } else if (typeof(value) === 'number' && parseInt(value) == value) {
        value = new BN(value);

    } else if (BN.isBN(value)) {
        //value = value

    } else if (isBigNumber(value)) {
        value = value._bn;

    } else if (convert.isArrayish(value)) {
        value = new BN(convert.hexlify(value).substring(2), 16);

    } else {
        throwError('invalid BigNumber value', { input: value });
    }

    defineProperty(this, '_bn', value);
}

defineProperty(BigNumber, 'constantNegativeOne', bigNumberify(-1));
defineProperty(BigNumber, 'constantZero', bigNumberify(0));
defineProperty(BigNumber, 'constantOne', bigNumberify(1));
defineProperty(BigNumber, 'constantTwo', bigNumberify(2));
defineProperty(BigNumber, 'constantWeiPerEther', bigNumberify(new BN('1000000000000000000')));

defineProperty(BigNumber.prototype, 'fromTwos', function(value) {
    return new BigNumber(this._bn.fromTwos(value));
});

defineProperty(BigNumber.prototype, 'toTwos', function(value) {
    return new BigNumber(this._bn.toTwos(value));
});

defineProperty(BigNumber.prototype, 'add', function(other) {
    return new BigNumber(this._bn.add(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'sub', function(other) {
    return new BigNumber(this._bn.sub(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'div', function(other) {
    return new BigNumber(this._bn.div(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'mul', function(other) {
    return new BigNumber(this._bn.mul(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'mod', function(other) {
    return new BigNumber(this._bn.mod(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'maskn', function(value) {
    return new BigNumber(this._bn.maskn(value));
});

defineProperty(BigNumber.prototype, 'eq', function(other) {
    return this._bn.eq(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'lt', function(other) {
    return this._bn.lt(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'lte', function(other) {
    return this._bn.lte(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'gte', function(other) {
    return this._bn.gte(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'isZero', function() {
    return this._bn.isZero();
});

defineProperty(BigNumber.prototype, 'toNumber', function(base) {
    return this._bn.toNumber();
});

defineProperty(BigNumber.prototype, 'toString', function() {
    //return this._bn.toString(base || 10);
    return this._bn.toString(10);
});

defineProperty(BigNumber.prototype, 'toHexString', function() {
    var hex = this._bn.toString(16);
    if (hex.length % 2) { hex = '0' + hex; }
    return '0x' + hex;
});

function isBigNumber(value) {
    return (value._bn && value._bn.mod);
}

function bigNumberify(value) {
    if (isBigNumber(value)) { return value; }
    return new BigNumber(value);
}

module.exports = {
    isBigNumber: isBigNumber,
    bigNumberify: bigNumberify
};
<commit_msg>Add gt to big numbers.<commit_after>/**
 *  BigNumber
 *
 *  A wrapper around the BN.js object. In the future we can swap out
 *  the underlying BN.js library for something smaller.
 */

var BN = require('bn.js');

var defineProperty = require('./properties').defineProperty;
var convert = require('./convert');
var throwError = require('./throw-error');

function BigNumber(value) {
    if (!(this instanceof BigNumber)) { throw new Error('missing new'); }

    if (convert.isHexString(value)) {
        if (value == '0x') { value = '0x0'; }
        value = new BN(value.substring(2), 16);

    } else if (typeof(value) === 'string' && value.match(/^-?[0-9]*$/)) {
        if (value == '') { value = '0'; }
        value = new BN(value);

    } else if (typeof(value) === 'number' && parseInt(value) == value) {
        value = new BN(value);

    } else if (BN.isBN(value)) {
        //value = value

    } else if (isBigNumber(value)) {
        value = value._bn;

    } else if (convert.isArrayish(value)) {
        value = new BN(convert.hexlify(value).substring(2), 16);

    } else {
        throwError('invalid BigNumber value', { input: value });
    }

    defineProperty(this, '_bn', value);
}

defineProperty(BigNumber, 'constantNegativeOne', bigNumberify(-1));
defineProperty(BigNumber, 'constantZero', bigNumberify(0));
defineProperty(BigNumber, 'constantOne', bigNumberify(1));
defineProperty(BigNumber, 'constantTwo', bigNumberify(2));
defineProperty(BigNumber, 'constantWeiPerEther', bigNumberify(new BN('1000000000000000000')));

defineProperty(BigNumber.prototype, 'fromTwos', function(value) {
    return new BigNumber(this._bn.fromTwos(value));
});

defineProperty(BigNumber.prototype, 'toTwos', function(value) {
    return new BigNumber(this._bn.toTwos(value));
});

defineProperty(BigNumber.prototype, 'add', function(other) {
    return new BigNumber(this._bn.add(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'sub', function(other) {
    return new BigNumber(this._bn.sub(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'div', function(other) {
    return new BigNumber(this._bn.div(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'mul', function(other) {
    return new BigNumber(this._bn.mul(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'mod', function(other) {
    return new BigNumber(this._bn.mod(bigNumberify(other)._bn));
});

defineProperty(BigNumber.prototype, 'maskn', function(value) {
    return new BigNumber(this._bn.maskn(value));
});

defineProperty(BigNumber.prototype, 'eq', function(other) {
    return this._bn.eq(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'lt', function(other) {
    return this._bn.lt(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'lte', function(other) {
    return this._bn.lte(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'gt', function(other) {
    return this._bn.gt(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'gte', function(other) {
    return this._bn.gte(bigNumberify(other)._bn);
});

defineProperty(BigNumber.prototype, 'isZero', function() {
    return this._bn.isZero();
});

defineProperty(BigNumber.prototype, 'toNumber', function(base) {
    return this._bn.toNumber();
});

defineProperty(BigNumber.prototype, 'toString', function() {
    //return this._bn.toString(base || 10);
    return this._bn.toString(10);
});

defineProperty(BigNumber.prototype, 'toHexString', function() {
    var hex = this._bn.toString(16);
    if (hex.length % 2) { hex = '0' + hex; }
    return '0x' + hex;
});

function isBigNumber(value) {
    return (value._bn && value._bn.mod);
}

function bigNumberify(value) {
    if (isBigNumber(value)) { return value; }
    return new BigNumber(value);
}

module.exports = {
    isBigNumber: isBigNumber,
    bigNumberify: bigNumberify
};
<|endoftext|>


<commit_before>/**
 * Zepto-Autocomplete
 * @author Yugal Jindle
 * @repo   https://github.com/yugal/zepto-autocomplete.git
 */

(function($) {
    'use strict';

    var methods = {
        init: function(settings) {
            var $currentOpt,
                $this = this, // selected jquery object
                settingsDefaults = {
                    data: [],
                    minLength: 1,
                    maxCount: Infinity,
                    extraClass: '',
                    appendTo: '',
                    position: true,
                    sort: function(data) {
                        return data.sort();
                    },
                    matcher: function(query, option) {
                        return option.toString().toLowerCase().indexOf(query.toLowerCase()) !== -1;
                    },
                    renderOption: function(option) {
                        return option.toString();
                    },
                    selectOption: function(option) {
                        return option.toString(); // Returned value updates the input
                    }
                },
                storedData = $this[0]._autoCompleteData;

            settings = $.extend(settingsDefaults, settings);

            function createOptionsDiv() {
                var position, $appendDiv, $optionsContainerDiv;
                $optionsContainerDiv = $('
');
                if(settings.position) {
                    position = {
                        left: $this.offset().left,
                        top: $this.offset().top + $this.height()
                    };
                    $optionsContainerDiv.css('position', 'absolute').css('top', position.top).css('left', position.left).css('zIndex', 1000);
                }
                // Configure $optionsContainerDiv
                $optionsContainerDiv.addClass('autocomplete-options-container').addClass(settings.extraClass);  // Adds class autocomplete-options-container
                $this.attr('data-autocomplete', 'true');
                $optionsContainerDiv.on('mousedown.autocomplete', 'div', chooseOption);
                $optionsContainerDiv.hide();
                $appendDiv = (settings.appendTo)? $(settings.appendTo):$('body');
                $appendDiv.append($optionsContainerDiv);
                settings.data = settings.sort(settings.data);
                // Save
                $this[0]._autoCompleteData = {
                    settings: settings,
                    $optionsContainerDiv: $optionsContainerDiv
                };
            }
            function resetOptionsDiv() {
                var $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv;
                $optionsContainerDiv.hide();
                $optionsContainerDiv.empty();
            }
            function closeOptionsDiv() {
                var $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv;
                $optionsContainerDiv[0].scrollTop = 0;
                $optionsContainerDiv.hide();
            }
            function chooseOption(event, optIndex) {
                var value, option,
                    settings = $this[0]._autoCompleteData.settings;
                optIndex = optIndex || $(event.target).closest('.autocomplete-opt').attr('data-opt-index');
                option = settings.data[optIndex];
                value = settings.selectOption(option);
                if(value) {
                    $this.val(option.toString());
                }
                closeOptionsDiv();
            }
            function changeCurrentOpt($other) {
                if($currentOpt) {
                    $currentOpt.removeClass('autocomplete-opt-curr');  // Removes class autocomplete-opt
                }
                $currentOpt = $other;
                $currentOpt.addClass('autocomplete-opt-curr');  // Adds class autocomplete-opt
            }
            function computeOptions() {
                var option,
                    $optionDiv,
                    $divs = $(), // List with jquery methods
                    value = $this.val(),
                    $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv,
                    settings = $this[0]._autoCompleteData.settings;
                resetOptionsDiv();
                // Honor minLength
                if(value.length<settings.minLength) {
                    return;
                }
                for(var i=0; i<settings.data.length && $divs.length<=settings.maxCount; i++) {
                    option = settings.data[i];
                    if (value && settings.matcher(value, option)) {
                        // Adds class autocomplete-opt
                        $optionDiv = $('

');
                        $optionDiv.attr('data-opt-index', i);
                        $optionDiv.append(settings.renderOption(option));
                        $divs = $divs.add($optionDiv);
                    }
                }
                if ($divs.length>0) {
                    $optionsContainerDiv.append($divs);
                    $optionsContainerDiv.show();
                    changeCurrentOpt($optionsContainerDiv.children().eq(0));
                }
            }
            function keyHandler(event) {
                var prev, next,
                    $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv;
                switch (event.keyCode) {
                    case 38: // Up
                        prev = $currentOpt.prev();
                        if(prev && prev.hasClass('autocomplete-opt')) {
                            changeCurrentOpt(prev);
                        }
                        break;
                    case 40: // Down
                        next = $currentOpt.next();
                        if(next && next.hasClass('autocomplete-opt')) {
                            changeCurrentOpt(next);
                        }
                        break;
                    case 13:  // Return
                        if($optionsContainerDiv.css('display') !== 'none') {
                            chooseOption(null, $currentOpt.attr('data-opt-index'));
                            resetOptionsDiv();
                        }
                        break;
                    default:
                        computeOptions();
                        break;
                }
            }

            // if not initialized
            if (!storedData) {
                createOptionsDiv();
                return $this.each(function() {
                    $this.bind('focus.autocomplete', computeOptions).bind('keyup.autocomplete', keyHandler).bind('blur.autocomplete', closeOptionsDiv);
                });
            }
        },
        destroy: function() {
            return this.each(function() {
                var $this = $(this);
                $this.unbind('.autocomplete');
                $this[0]._autoCompleteData.$optionsContainerDiv.remove();
                delete $this[0]._autoCompleteData;
            });
        },
        add: function(option) {
            var $this = $(this),
                options,
                settings = $this[0]._autoCompleteData.settings;
            $this = this;
            // create unique
            function unique(arr) {
                var uniqArr = [];
                $.each(arr, function(idx, el) {
                    if($.inArray(el, uniqArr) === -1) {
                        uniqArr.push(el);
                    }
                });
                return uniqArr;
            }
            // option or options
            options = (option instanceof Array)? option:[options];
            options = unique(options.concat(settings.data));
            settings.data = settings.sort(settings.data);
        }
    };

    $.fn.autocomplete = function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error(method + ' :: Not supported');
        }
    };
})(Zepto || jQuery);
<commit_msg>Fixed a bug in selectOption<commit_after>/**
 * Zepto-Autocomplete
 * @author Yugal Jindle
 * @repo   https://github.com/yugal/zepto-autocomplete.git
 */

(function($) {
    'use strict';

    var methods = {
        init: function(settings) {
            var $currentOpt,
                $this = this, // selected jquery object
                settingsDefaults = {
                    data: [],
                    minLength: 1,
                    maxCount: Infinity,
                    extraClass: '',
                    appendTo: '',
                    position: true,
                    sort: function(data) {
                        return data.sort();
                    },
                    matcher: function(query, option) {
                        return option.toString().toLowerCase().indexOf(query.toLowerCase()) !== -1;
                    },
                    renderOption: function(option) {
                        return option.toString();
                    },
                    selectOption: function(option) {
                        return option.toString(); // Returned value updates the input
                    }
                },
                storedData = $this[0]._autoCompleteData;

            settings = $.extend(settingsDefaults, settings);

            function createOptionsDiv() {
                var position, $appendDiv, $optionsContainerDiv;
                $optionsContainerDiv = $('
');
                if(settings.position) {
                    position = {
                        left: $this.offset().left,
                        top: $this.offset().top + $this.height()
                    };
                    $optionsContainerDiv.css('position', 'absolute').css('top', position.top).css('left', position.left).css('zIndex', 1000);
                }
                // Configure $optionsContainerDiv
                $optionsContainerDiv.addClass('autocomplete-options-container').addClass(settings.extraClass);  // Adds class autocomplete-options-container
                $this.attr('data-autocomplete', 'true');
                $optionsContainerDiv.on('mousedown.autocomplete', 'div', chooseOption);
                $optionsContainerDiv.hide();
                $appendDiv = (settings.appendTo)? $(settings.appendTo):$('body');
                $appendDiv.append($optionsContainerDiv);
                settings.data = settings.sort(settings.data);
                // Save
                $this[0]._autoCompleteData = {
                    settings: settings,
                    $optionsContainerDiv: $optionsContainerDiv
                };
            }
            function resetOptionsDiv() {
                var $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv;
                $optionsContainerDiv.hide();
                $optionsContainerDiv.empty();
            }
            function closeOptionsDiv() {
                var $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv;
                $optionsContainerDiv[0].scrollTop = 0;
                $optionsContainerDiv.hide();
            }
            function chooseOption(event, optIndex) {
                var value, option,
                    settings = $this[0]._autoCompleteData.settings;
                optIndex = optIndex || $(event.target).closest('.autocomplete-opt').attr('data-opt-index');
                option = settings.data[optIndex];
                value = settings.selectOption(option);
                if(value !== undefined) {
                    $this.val(option.toString());
                }
                closeOptionsDiv();
            }
            function changeCurrentOpt($other) {
                if($currentOpt) {
                    $currentOpt.removeClass('autocomplete-opt-curr');  // Removes class autocomplete-opt
                }
                $currentOpt = $other;
                $currentOpt.addClass('autocomplete-opt-curr');  // Adds class autocomplete-opt
            }
            function computeOptions() {
                var option,
                    $optionDiv,
                    $divs = $(), // List with jquery methods
                    value = $this.val(),
                    $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv,
                    settings = $this[0]._autoCompleteData.settings;
                resetOptionsDiv();
                // Honor minLength
                if(value.length<settings.minLength) {
                    return;
                }
                for(var i=0; i<settings.data.length && $divs.length<=settings.maxCount; i++) {
                    option = settings.data[i];
                    if (value && settings.matcher(value, option)) {
                        // Adds class autocomplete-opt
                        $optionDiv = $('

');
                        $optionDiv.attr('data-opt-index', i);
                        $optionDiv.append(settings.renderOption(option));
                        $divs = $divs.add($optionDiv);
                    }
                }
                if ($divs.length>0) {
                    $optionsContainerDiv.append($divs);
                    $optionsContainerDiv.show();
                    changeCurrentOpt($optionsContainerDiv.children().eq(0));
                }
            }
            function keyHandler(event) {
                var prev, next,
                    $optionsContainerDiv = $this[0]._autoCompleteData.$optionsContainerDiv;
                switch (event.keyCode) {
                    case 38: // Up
                        prev = $currentOpt.prev();
                        if(prev && prev.hasClass('autocomplete-opt')) {
                            changeCurrentOpt(prev);
                        }
                        break;
                    case 40: // Down
                        next = $currentOpt.next();
                        if(next && next.hasClass('autocomplete-opt')) {
                            changeCurrentOpt(next);
                        }
                        break;
                    case 13:  // Return
                        if($optionsContainerDiv.css('display') !== 'none') {
                            chooseOption(null, $currentOpt.attr('data-opt-index'));
                            resetOptionsDiv();
                        }
                        break;
                    default:
                        computeOptions();
                        break;
                }
            }

            // if not initialized
            if (!storedData) {
                createOptionsDiv();
                return $this.each(function() {
                    $this.bind('focus.autocomplete', computeOptions).bind('keyup.autocomplete', keyHandler).bind('blur.autocomplete', closeOptionsDiv);
                });
            }
        },
        destroy: function() {
            return this.each(function() {
                var $this = $(this);
                $this.unbind('.autocomplete');
                $this[0]._autoCompleteData.$optionsContainerDiv.remove();
                delete $this[0]._autoCompleteData;
            });
        },
        add: function(option) {
            var $this = $(this),
                options,
                settings = $this[0]._autoCompleteData.settings;
            $this = this;
            // create unique
            function unique(arr) {
                var uniqArr = [];
                $.each(arr, function(idx, el) {
                    if($.inArray(el, uniqArr) === -1) {
                        uniqArr.push(el);
                    }
                });
                return uniqArr;
            }
            // option or options
            options = (option instanceof Array)? option:[options];
            options = unique(options.concat(settings.data));
            settings.data = settings.sort(settings.data);
        }
    };

    $.fn.autocomplete = function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error(method + ' :: Not supported');
        }
    };
})(Zepto || jQuery);
<|endoftext|>


<commit_before>var fs = require('fs');
var Minimatch = require('minimatch').Minimatch;
var resolve = require('path').resolve;
var relative = require('path').relative;
var gfPath = require('./gulpfile-path');
var RcLoader = require('rcloader');
var PluginError = require('gulp-util').PluginError;

var noSlashesRE = /^[^\/]*\/?$/;

var ignoreLoader = new RcLoader('.jshintignore', {}, {
  loader: function (path) {
    // .jshintignore is a line-delimited list of patterns
    // convert to an array and filter empty lines
    var contents = fs.readFileSync(path, 'utf8');
    return {
      ignoreFile: path,
      patterns: contents.toString('utf8')
        .split(/\r?\n/)
        .filter(function (line) { return !!line.trim(); })
    };
  }
});

// get the .jshintignore closest to the directory containing the gruntFile
var cfg = ignoreLoader.for(gfPath);

module.exports = (function () {
  if (!cfg.ignoreFile) {
    return function (file, cb) {
      cb(null, false);
    };
  }

  var mms = {};
  cfg.patterns.forEach(function (pattern) {
    mms[pattern] = new Minimatch(pattern, { nocase: true });
  });

  return function check(file, cb) {
    if (file.isNull()) return cb(null, true); // ignore null files
    if (file.isStream()) return cb(new PluginError('gulp-jshint', 'Streaming not supported')); // throw an error

    var ignored = cfg.patterns.some(function (pattern) {
      var resolvedPath = resolve(file.path);
      var relativePath = relative(file.base, file.path);
      var mm = mms[pattern];

      if (resolvedPath === pattern) return true;
      if (mm.match(resolvedPath)) return true;

      if (noSlashesRE.test(pattern)) {
        var relPath = relative(pattern, relativePath);
        if (relPath.substring(0, 2) !== '..') return true;
      }
    });

    if (!ignored) return cb(null, false);

    cb(null, true);
  };
}());<commit_msg>fix typo in comment in `file-ignored.js`<commit_after>var fs = require('fs');
var Minimatch = require('minimatch').Minimatch;
var resolve = require('path').resolve;
var relative = require('path').relative;
var gfPath = require('./gulpfile-path');
var RcLoader = require('rcloader');
var PluginError = require('gulp-util').PluginError;

var noSlashesRE = /^[^\/]*\/?$/;

var ignoreLoader = new RcLoader('.jshintignore', {}, {
  loader: function (path) {
    // .jshintignore is a line-delimited list of patterns
    // convert to an array and filter empty lines
    var contents = fs.readFileSync(path, 'utf8');
    return {
      ignoreFile: path,
      patterns: contents.toString('utf8')
        .split(/\r?\n/)
        .filter(function (line) { return !!line.trim(); })
    };
  }
});

// get the .jshintignore closest to the directory containing the gulpfile
var cfg = ignoreLoader.for(gfPath);

module.exports = (function () {
  if (!cfg.ignoreFile) {
    return function (file, cb) {
      cb(null, false);
    };
  }

  var mms = {};
  cfg.patterns.forEach(function (pattern) {
    mms[pattern] = new Minimatch(pattern, { nocase: true });
  });

  return function check(file, cb) {
    if (file.isNull()) return cb(null, true); // ignore null files
    if (file.isStream()) return cb(new PluginError('gulp-jshint', 'Streaming not supported')); // throw an error

    var ignored = cfg.patterns.some(function (pattern) {
      var resolvedPath = resolve(file.path);
      var relativePath = relative(file.base, file.path);
      var mm = mms[pattern];

      if (resolvedPath === pattern) return true;
      if (mm.match(resolvedPath)) return true;

      if (noSlashesRE.test(pattern)) {
        var relPath = relative(pattern, relativePath);
        if (relPath.substring(0, 2) !== '..') return true;
      }
    });

    if (!ignored) return cb(null, false);

    cb(null, true);
  };
}());
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 Google 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.
 */

//
// Refine COUNT to perform two optimizations:
// (1) Perform a key-only query when the sink is a COUNT;
// (2) Do not bother construction FObjects on query result batches.
//

foam.CLASS({
  refines: 'foam.mlang.sink.Count',

  methods: [
    {
      name: 'decorateDatastoreQuery',
      documentation: `Optimize plain COUNT() queries by requesting keys only.
          This entails filling in runQuery.projection [1] with the magic
          "__key__" key.

          [1] https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#Projection`,
      code: function(query) {
        query.projection = [ { property: { name: "__key__" } } ];
      }
    },
    {
      name: 'fromDatastoreEntityResults',
      documentation: `Optimize plain COUNT() queries by not constructing
          FObjects to store results. Input paramter is
          QueryResultBatch.entityResults [1].

          [1] https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#QueryResultBatch`,
      code: function(entityResults) { this.value += entityResults.length; }
    }
  ]
});

//
// Refine Property and DOT to produce datastore property.
//

foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    {
      name: 'toDatastorePropertyReference',
      documentation:
      function() {/*
                    Provides a PropertyReference for the Cloud Datastore REST
                    API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#PropertyReference
                  */},
      code: function() { return { name: this.name }; }
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.expr.Dot',

  methods: [
    {
      name: 'toDatastorePropertyReference',
      code: function() {
        return { name: this.arg1.name + '.' + this.arg2.name };
      }
    }
  ]
});

//
// Refine constants to produce datastore values. This is needed because
// mLangs will attempt to access datastore values on the arguments.
//

foam.CLASS({
  refines: 'foam.mlang.Constant',

  methods: [
    {
      name: 'toDatastoreValue',
      documentation:
      function() {/*
                    Provides Value for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#Value
                  */},
      code: function() {
        return com.google.cloud.datastore.toDatastoreValue(this.value);
      }
    }
  ]
});

//
// Refine AND and subset of binary ops to support toDatastoreFilter().
//

foam.CLASS({
  refines: 'foam.mlang.predicate.AbstractPredicate',

  methods: [
    {
      name: 'toDatastoreFilter',
      documentation:
      function() {/*
                    Provides Filter for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#filter
                  */},
      code: function() {
        throw new Error('Predicate not supported in datastore ' +
            'implementation: ' + this.cls_.id);
      }
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.And',

  methods: [
    {
      name: 'toOwnDatastoreFilter',
      documentation:
      function() {/*
                    Provides (CompositeFilter|PropertyFilter) for a Filter in
                    the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#filter
                  */},
      code: function() {
        var filters = new Array(this.args.length);

        for ( var i = 0; i < this.args.length; i++ ) {
          filters[i] = this.args[i].toDatastoreFilter();
        }

        return { op: 'AND', filters: filters };
      }
    },

    function toDatastoreFilter() {
      return { compositeFilter: this.toOwnDatastoreFilter() };
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Binary',

  properties: [
    {
      class: 'String',
      function() {/*
                    Provides Operator for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#Operator_1
                  */},
      name: 'datastoreOpName'
    }
  ],

  methods: [
    function toOwnDatastoreFilter() {
      foam.assert(foam.core.Property.isInstance(this.arg1),
          'Left-hand-side of datastore binary op is not a property');
      foam.assert(this.datastoreOpName,
          'Predicate has no datastore op name:', this.cls_.id);

      return {
        property: this.arg1.toDatastorePropertyReference(),
        op: this.datastoreOpName,
        value: com.google.cloud.datastore.toDatastoreValue(this.arg2)
      };
    },

    function toDatastoreFilter() {
      return { propertyFilter: this.toOwnDatastoreFilter() };
    }
  ]
});

//
// Use above Binary implementation on Google Cloud Datastore-supported
// predicates by refining them with a particular "datastoreOpName" value.
//

foam.CLASS({refines: 'foam.mlang.predicate.Eq',  properties: [ {class: 'String', name: 'datastoreOpName', value: 'EQUAL' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Lt',  properties: [ {class: 'String', name: 'datastoreOpName', value: 'LESS_THAN' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Lte', properties: [ {class: 'String', name: 'datastoreOpName', value: 'LESS_THAN_OR_EQUAL' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Gt',  properties: [ {class: 'String', name: 'datastoreOpName', value: 'GREATER_THAN' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Gte', properties: [ {class: 'String', name: 'datastoreOpName', value: 'GREATER_THAN_OR_EQUAL' }] });

//
// Refine properties and ThenBy mLang to behave as orderings.
//

foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    {
      name: 'toDatastoreOrder',
      documentation:
      function() {/*
                    Provides PropertyOrder for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#propertyorder
                  */},
      code: function() {
      return this.orderDirection() === 1 ?
          [ { property: { name: this.name } } ] :
          [ { property: { name: this.name }, direction: 'DESCENDING' } ];
      }
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.ThenBy',

  methods: [
    function toDatastoreOrder() {
      var order1 = this.arg1.toDatastoreOrder();
      var order2 = this.arg2.toDatastoreOrder();
      return order1.concat(order2);
    }
  ]
});
<commit_msg>Cloud Datastore / JS: Add proper ordering support, including DESC()<commit_after>/**
 * @license
 * Copyright 2017 Google 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.
 */

//
// Refine COUNT to perform two optimizations:
// (1) Perform a key-only query when the sink is a COUNT;
// (2) Do not bother construction FObjects on query result batches.
//

foam.CLASS({
  refines: 'foam.mlang.sink.Count',

  methods: [
    {
      name: 'decorateDatastoreQuery',
      documentation: `Optimize plain COUNT() queries by requesting keys only.
          This entails filling in runQuery.projection [1] with the magic
          "__key__" key.

          [1] https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#Projection`,
      code: function(query) {
        query.projection = [ { property: { name: "__key__" } } ];
      }
    },
    {
      name: 'fromDatastoreEntityResults',
      documentation: `Optimize plain COUNT() queries by not constructing
          FObjects to store results. Input paramter is
          QueryResultBatch.entityResults [1].

          [1] https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#QueryResultBatch`,
      code: function(entityResults) { this.value += entityResults.length; }
    }
  ]
});

//
// Refine Property and DOT to produce datastore property.
//

foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    {
      name: 'toDatastorePropertyReference',
      documentation:
      function() {/*
                    Provides a PropertyReference for the Cloud Datastore REST
                    API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#PropertyReference
                  */},
      code: function() { return { name: this.name }; }
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.expr.Dot',

  methods: [
    {
      name: 'toDatastorePropertyReference',
      code: function() {
        return { name: this.arg1.name + '.' + this.arg2.name };
      }
    }
  ]
});

//
// Refine constants to produce datastore values. This is needed because
// mLangs will attempt to access datastore values on the arguments.
//

foam.CLASS({
  refines: 'foam.mlang.Constant',

  methods: [
    {
      name: 'toDatastoreValue',
      documentation:
      function() {/*
                    Provides Value for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#Value
                  */},
      code: function() {
        return com.google.cloud.datastore.toDatastoreValue(this.value);
      }
    }
  ]
});

//
// Refine AND and subset of binary ops to support toDatastoreFilter().
//

foam.CLASS({
  refines: 'foam.mlang.predicate.AbstractPredicate',

  methods: [
    {
      name: 'toDatastoreFilter',
      documentation:
      function() {/*
                    Provides Filter for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#filter
                  */},
      code: function() {
        throw new Error('Predicate not supported in datastore ' +
            'implementation: ' + this.cls_.id);
      }
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.And',

  methods: [
    {
      name: 'toOwnDatastoreFilter',
      documentation:
      function() {/*
                    Provides (CompositeFilter|PropertyFilter) for a Filter in
                    the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#filter
                  */},
      code: function() {
        var filters = new Array(this.args.length);

        for ( var i = 0; i < this.args.length; i++ ) {
          filters[i] = this.args[i].toDatastoreFilter();
        }

        return { op: 'AND', filters: filters };
      }
    },

    function toDatastoreFilter() {
      return { compositeFilter: this.toOwnDatastoreFilter() };
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Binary',

  properties: [
    {
      class: 'String',
      function() {/*
                    Provides Operator for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#Operator_1
                  */},
      name: 'datastoreOpName'
    }
  ],

  methods: [
    function toOwnDatastoreFilter() {
      foam.assert(foam.core.Property.isInstance(this.arg1),
          'Left-hand-side of datastore binary op is not a property');
      foam.assert(this.datastoreOpName,
          'Predicate has no datastore op name:', this.cls_.id);

      return {
        property: this.arg1.toDatastorePropertyReference(),
        op: this.datastoreOpName,
        value: com.google.cloud.datastore.toDatastoreValue(this.arg2)
      };
    },

    function toDatastoreFilter() {
      return { propertyFilter: this.toOwnDatastoreFilter() };
    }
  ]
});

//
// Use above Binary implementation on Google Cloud Datastore-supported
// predicates by refining them with a particular "datastoreOpName" value.
//

foam.CLASS({refines: 'foam.mlang.predicate.Eq',  properties: [ {class: 'String', name: 'datastoreOpName', value: 'EQUAL' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Lt',  properties: [ {class: 'String', name: 'datastoreOpName', value: 'LESS_THAN' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Lte', properties: [ {class: 'String', name: 'datastoreOpName', value: 'LESS_THAN_OR_EQUAL' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Gt',  properties: [ {class: 'String', name: 'datastoreOpName', value: 'GREATER_THAN' }] });
foam.CLASS({refines: 'foam.mlang.predicate.Gte', properties: [ {class: 'String', name: 'datastoreOpName', value: 'GREATER_THAN_OR_EQUAL' }] });

//
// Refine properties and ThenBy mLang to behave as orderings.
//

foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    {
      name: 'toDatastoreOrder',
      documentation:
      function() {/*
                    Provides PropertyOrder for the Cloud Datastore REST API.
                    https://cloud.google.com/datastore/docs/reference/rest/v1/projects/runQuery#propertyorder
                  */},
      code: function(opt_orderDirection) {
        var orderDirection = opt_orderDirection || 1;
        return orderDirection === 1 ?
            [ { property: { name: this.name } } ] :
            [ { property: { name: this.name }, direction: 'DESCENDING' } ];
      }
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.Desc',

  methods: [
    function toDatastoreOrder(opt_orderDirection) {
      var orderDirection = -1 * (opt_orderDirection || 1);
      return this.arg1.toDatastoreOrder(orderDirection);
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.ThenBy',

  methods: [
    function toDatastoreOrder(opt_orderDirection) {
      var order1 = this.arg1.toDatastoreOrder(opt_orderDirection);
      var order2 = this.arg2.toDatastoreOrder(opt_orderDirection);
      return order1.concat(order2);
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @desc set() sets a cookie with optional days
 */

const cookie = {
  set(name, value, optDays) {
    'use strict';
    let expires = '';
    if (optDays) {
      const date = new Date();
      date.setTime(date.getTime() + (optDays * 24 * 60 * 60 * 1000));
      expires = '; expires=' + date.toGMTString();
    }
    document.cookie = name + '=' + value + expires + '; path=/';
  },
  get(name) {
    'use strict';
    const nameEQ = name + '=';
    const ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) === ' ') c = c.substring(1, c.length);
      if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
  },
  del(name) {
    document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;
  },
};

export default cookie;
<commit_msg>updates description<commit_after>/**
 * @desc set() sets a cookie with optional days
 *  @param {String} name - the name of the cookie
 *  @param {String} value - the value of the cookie
 *  @param {Number} optDays - days the cookie will exist for
 *    NOTE: Not passing optDays will create a "Session Cookie"
 *  @return {Undefined}

 * @desc get() gets value of cookie
 *  @param {String} name - name of cookie to get
 *  @return {String|Null} - string value of cookie NOT A BOOL!
 *
 * @desc del() removes cookie
 *  @param {String} name - name of cookie to delete
 *  @return {Undefined}
 */

const cookie = {
  set(name, value, optDays) {
    'use strict';
    let expires = '';
    if (optDays) {
      const date = new Date();
      date.setTime(date.getTime() + (optDays * 24 * 60 * 60 * 1000));
      expires = '; expires=' + date.toGMTString();
    }
    document.cookie = name + '=' + value + expires + '; path=/';
  },
  get(name) {
    'use strict';
    const nameEQ = name + '=';
    const ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) === ' ') c = c.substring(1, c.length);
      if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
  },
  del(name) {
    document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;
  },
};

export default cookie;
<|endoftext|>


<commit_before>//= require bootstrap

var Steno = {
  sourceTextEd: null,
  xmlEd: null,
  syncScrolling: true,

  init: function() {
    $('#parse-btn').on('click', Steno.parseSource);
    $('#source-doc-html').on('scroll', Steno.htmlScroll);
    $('#metadata-step button.next-step').on('click', function(e) {
      e.preventDefault();
      $('ul.steps li:eq(1) a').tab('show');
    });
    $('#text-step button.next-step').on('click', function() {
      e.preventDefault();
      $('ul.steps li:eq(2) a').tab('show');
    });

    // source text editor
    var ed;
    Steno.sourceTextEd = ed = ace.edit("source-doc-text");

    ed.setTheme("ace/theme/chrome");
    ed.setShowPrintMargin(false);

    var sess = ed.getSession();
    sess.setMode("ace/mode/text");
    sess.setUseWrapMode(true);

    // xml editor
    Steno.xmlEd = ed = ace.edit("doc-xml");

    ed.setTheme("ace/theme/chrome");
    ed.setShowPrintMargin(false);

    sess = ed.getSession();
    sess.setMode("ace/mode/xml");
    sess.setUseWrapMode(true);
  },

  /**
   * Update based on a parse of the source text
   */
  parsedSource: function(data) {
    console.log(data);

    // update the source text
    var ed = Steno.sourceTextEd;
    var posn = ed.getCursorPosition();
    var sourceChanged = ed.getValue() != data.source_text;

    if (sourceChanged) {
      ed.setValue(data.source_text);
      ed.clearSelection();
    }

    if (Steno.setParseErrors(data.parse_errors)) {
      // errors
      ed.gotoLine(data.parse_errors[0].line, data.parse_errors[0].column);
      ed.focus();
    } else {
      // no errors
      // kick of a background render
      Steno.renderSourceOutput(data.xml);

      if (sourceChanged) {
        ed.gotoLine(posn.row+1, posn.column);
      }
    }
    ed.renderer.scrollCursorIntoView();

    // update the XML
    ed = Steno.xmlEd;
    ed.setValue(data.xml);
    ed.clearSelection();

  },

  setParseErrors: function(errors) {
    errors = $.map(errors, function(e) {
      if (e.line && e.column) {
        return {row: e.line-1, column: e.column, text: e.message, type: "error"};
      } else {
        return {};
      }
    });
    Steno.sourceTextEd.getSession().setAnnotations(errors);

    return (errors.length > 0);
  },

  /**
   * Parse the source text of the document.
   */
  parseSource: function(e) {
    e.preventDefault();

    var btn = $('#parse-btn').addClass('disabled');
    var btnText = btn.html();
    btn.html('<i class="fa fa-cog fa-spin">');

    var data = $('#source-form').serializeArray();
    data.push({name: 'doc[source_text]', value: Steno.sourceTextEd.getValue()});

    $.ajax('/parse', {
      method: 'POST',
      data: data,
      success: Steno.parsedSource,
      complete: function() {
        btn.html(btnText);
        $('#parse-btn').removeClass('disabled');
      }
    });
  },

  // The HTML section scrolled, if we're syncing scrolling,
  // handle it.
  htmlScroll: function() {
    // TODO: scroll all the others, too
    if (Steno.syncScrolling) {
      var $html = $('#source-doc-html');

      var perc = $html.scrollTop() / $html[0].scrollHeight;
      var line = Steno.sourceTextEd.getSession().getLength() * perc;

      Steno.sourceTextEd.scrollToLine(line, false, true);
    }
  },

  /**
   * Render the source-to-xml output
   */
  renderSourceOutput: function(xml) {
    $.ajax('/render', {
      method: 'POST',
      data: {'doc[xml]': xml},
      success: function(data) {
        // update the HTML
        $('#source-doc-html').html(data.html);
      },
    });
  },
};

$(function() {
  Steno.init();
});
<commit_msg>Bug fixes on scrolling.<commit_after>//= require bootstrap

var Steno = {
  sourceTextEd: null,
  xmlEd: null,
  syncScrolling: true,

  init: function() {
    $('#parse-btn').on('click', Steno.parseSource);
    $('#source-doc-html').on('scroll', Steno.htmlScroll);
    $('#metadata-step button.next-step').on('click', function(e) {
      e.preventDefault();
      $('ul.steps li:eq(1) a').tab('show');
    });
    $('#text-step button.next-step').on('click', function(e) {
      e.preventDefault();
      $('ul.steps li:eq(2) a').tab('show');
    });

    // source text editor
    var ed;
    Steno.sourceTextEd = ed = ace.edit("source-doc-text");

    ed.setTheme("ace/theme/chrome");
    ed.setShowPrintMargin(false);

    var sess = ed.getSession();
    sess.setMode("ace/mode/text");
    sess.setUseWrapMode(true);

    // xml editor
    Steno.xmlEd = ed = ace.edit("doc-xml");

    ed.setTheme("ace/theme/chrome");
    ed.setShowPrintMargin(false);

    sess = ed.getSession();
    sess.setMode("ace/mode/xml");
    sess.setUseWrapMode(true);
  },

  /**
   * Update based on a parse of the source text
   */
  parsedSource: function(data) {
    console.log(data);

    // update the source text
    var ed = Steno.sourceTextEd;
    var posn = ed.getCursorPosition();
    var sourceChanged = ed.getValue() != data.source_text;

    if (sourceChanged) {
      ed.setValue(data.source_text);
      ed.clearSelection();
    }

    if (Steno.setParseErrors(data.parse_errors)) {
      // errors
      ed.gotoLine(data.parse_errors[0].line, data.parse_errors[0].column);
      ed.focus();
    } else {
      // no errors
      // kick of a background render
      Steno.renderSourceOutput(data.xml);

      if (sourceChanged) {
        ed.gotoLine(posn.row+1, posn.column);
      }
    }
    ed.renderer.scrollCursorIntoView();

    // update the XML
    ed = Steno.xmlEd;
    ed.setValue(data.xml);
    ed.clearSelection();

  },

  setParseErrors: function(errors) {
    errors = $.map(errors, function(e) {
      if (e.line && e.column) {
        return {row: e.line-1, column: e.column, text: e.message, type: "error"};
      } else {
        return {};
      }
    });
    Steno.sourceTextEd.getSession().setAnnotations(errors);

    return (errors.length > 0);
  },

  /**
   * Parse the source text of the document.
   */
  parseSource: function(e) {
    e.preventDefault();

    var btn = $('#parse-btn').addClass('disabled');
    var btnText = btn.html();
    btn.html('<i class="fa fa-cog fa-spin">');

    var data = $('#source-form').serializeArray();
    data.push({name: 'doc[source_text]', value: Steno.sourceTextEd.getValue()});

    $.ajax('/parse', {
      method: 'POST',
      data: data,
      success: Steno.parsedSource,
      complete: function() {
        btn.html(btnText);
        $('#parse-btn').removeClass('disabled');
      }
    });
  },

  // The HTML section scrolled, if we're syncing scrolling,
  // handle it.
  htmlScroll: function() {
    // TODO: scroll all the others, too
    if (Steno.syncScrolling) {
      var $html = $('#source-doc-html');

      var perc = $html.scrollTop() / $html[0].scrollHeight;
      var line = Math.floor(Steno.sourceTextEd.getSession().getLength() * perc);

      Steno.sourceTextEd.scrollToLine(line, false, true);
    }
  },

  /**
   * Render the source-to-xml output
   */
  renderSourceOutput: function(xml) {
    $.ajax('/render', {
      method: 'POST',
      data: {'doc[xml]': xml},
      success: function(data) {
        // update the HTML
        $('#source-doc-html').html(data.html);
      },
    });
  },
};

$(function() {
  Steno.init();
});
<|endoftext|>


<commit_before>const BinaryPjax          = require('./binary_pjax');
const pages_config        = require('./binary_pages');
const Client              = require('./client');
const GTM                 = require('./gtm');
const Header              = require('./header');
const Login               = require('./login');
const NetworkMonitor      = require('./network_monitor');
const Page                = require('./page');
const BinarySocket        = require('./socket');
const ContentVisibility   = require('../common/content_visibility');
const getElementById      = require('../../_common/common_functions').getElementById;
const localize            = require('../../_common/localize').localize;
const ScrollToAnchor      = require('../../_common/scroll_to_anchor');
const isStorageSupported  = require('../../_common/storage').isStorageSupported;
const urlFor              = require('../../_common/url').urlFor;
const createElement       = require('../../_common/utility').createElement;

const BinaryLoader = (() => {
    let container;
    let active_script = null;

    const init = () => {
        if (!/\.html$/i.test(window.location.pathname)) {
            window.location.pathname += '.html';
            return;
        }

        if (!isStorageSupported(localStorage) || !isStorageSupported(sessionStorage)) {
            Header.displayNotification(localize('[_1] requires your browser\'s web storage to be enabled in order to function properly. Please enable it or exit private browsing mode.', ['Binary.com']),
                true, 'STORAGE_NOT_SUPPORTED');
            getElementById('btn_login').classList.add('button-disabled');
        }

        Page.showNotificationOutdatedBrowser();

        Client.init();
        NetworkMonitor.init();

        container = getElementById('content-holder');
        container.addEventListener('binarypjax:before', beforeContentChange);
        container.addEventListener('binarypjax:after',  afterContentChange);
        BinaryPjax.init(container, '#content');

        document.body.addEventListener('click', (e) => {
            if (!e.target) return;
            const link_el = e.target.closest('a');
            if (link_el) {
                const destination = new URL(link_el.href);
                // TODO: check for subdomains
                if (destination.host !== window.location.host) {
                    const should_proceed = window.confirm(localize('You will be redirected to a third-party website which is not owned by Binary.com. Click OK to proceed.'));
                    if (!should_proceed) e.preventDefault();
                }
            }
        });
    };

    const beforeContentChange = () => {
        if (active_script) {
            BinarySocket.removeOnDisconnect();
            if (typeof active_script.onUnload === 'function') {
                active_script.onUnload();
            }
            active_script = null;
        }
        ScrollToAnchor.cleanup();
    };

    const afterContentChange = (e) => {
        Page.onLoad();
        GTM.pushDataLayer();

        const this_page = e.detail.getAttribute('data-page');
        if (this_page in pages_config) {
            loadHandler(pages_config[this_page]);
        } else if (/\/get-started\//i.test(window.location.pathname)) {
            loadHandler(pages_config['get-started']);
        }

        ContentVisibility.init();
        ScrollToAnchor.init();
    };

    const error_messages = {
        login       : () => localize('Please [_1]log in[_2] or [_3]sign up[_2] to view this page.', [`<a href="${'javascript:;'}">`, '', `<a href="${urlFor()}">`]),
        only_virtual: 'Sorry, this feature is available to virtual accounts only.',
        only_real   : 'This feature is not relevant to virtual-money accounts.',
    };

    const loadHandler = (config) => {
        active_script = config.module;
        if (config.is_authenticated) {
            if (!Client.isLoggedIn()) {
                displayMessage(error_messages.login());
            } else {
                BinarySocket.wait('authorize')
                    .then((response) => {
                        if (response.error) {
                            displayMessage(error_messages.login());
                        } else if (config.only_virtual && !Client.get('is_virtual')) {
                            displayMessage(error_messages.only_virtual);
                        } else if (config.only_real && Client.get('is_virtual')) {
                            displayMessage(error_messages.only_real);
                        } else {
                            loadActiveScript(config);
                        }
                    });
            }
        } else if (config.not_authenticated && Client.isLoggedIn()) {
            BinaryPjax.load(Client.defaultRedirectUrl(), true);
        } else {
            loadActiveScript(config);
        }
        BinarySocket.setOnDisconnect(active_script.onDisconnect);
    };

    const loadActiveScript = (config) => {
        if (active_script && typeof active_script.onLoad === 'function') {
            // only pages that call formatMoney should wait for website_status
            if (config.needs_currency) {
                BinarySocket.wait('website_status').then(() => {
                    active_script.onLoad();
                });
            } else {
                active_script.onLoad();
            }
        }
    };

    const displayMessage = (message) => {
        const content = container.querySelector('#content .container');
        if (!content) {
            return;
        }

        const div_container = createElement('div', { class: 'logged_out_title_container', html: content.getElementsByTagName('h1')[0] });
        const div_notice    = createElement('p', { class: 'center-text notice-msg', html: localize(message) });

        div_container.appendChild(div_notice);

        content.html(div_container);

        const link = content.getElementsByTagName('a')[0];
        if (link) {
            link.addEventListener('click', () => { Login.redirectToLogin(); });
        }
    };

    return {
        init,
    };
})();

module.exports = BinaryLoader;
<commit_msg>check for binary subdomains, refactor<commit_after>const BinaryPjax          = require('./binary_pjax');
const pages_config        = require('./binary_pages');
const Client              = require('./client');
const GTM                 = require('./gtm');
const Header              = require('./header');
const Login               = require('./login');
const NetworkMonitor      = require('./network_monitor');
const Page                = require('./page');
const BinarySocket        = require('./socket');
const ContentVisibility   = require('../common/content_visibility');
const getElementById      = require('../../_common/common_functions').getElementById;
const localize            = require('../../_common/localize').localize;
const ScrollToAnchor      = require('../../_common/scroll_to_anchor');
const isStorageSupported  = require('../../_common/storage').isStorageSupported;
const urlFor              = require('../../_common/url').urlFor;
const createElement       = require('../../_common/utility').createElement;

const BinaryLoader = (() => {
    let container;
    let active_script = null;

    const init = () => {
        if (!/\.html$/i.test(window.location.pathname)) {
            window.location.pathname += '.html';
            return;
        }

        if (!isStorageSupported(localStorage) || !isStorageSupported(sessionStorage)) {
            Header.displayNotification(localize('[_1] requires your browser\'s web storage to be enabled in order to function properly. Please enable it or exit private browsing mode.', ['Binary.com']),
                true, 'STORAGE_NOT_SUPPORTED');
            getElementById('btn_login').classList.add('button-disabled');
        }

        Page.showNotificationOutdatedBrowser();

        Client.init();
        NetworkMonitor.init();

        container = getElementById('content-holder');
        container.addEventListener('binarypjax:before', beforeContentChange);
        container.addEventListener('binarypjax:after',  afterContentChange);
        BinaryPjax.init(container, '#content');

        function isThirdPartyLink(href) {
            const destination = new URL(href);
            return !/^.*\.binary\.com$/.test(destination.host)
                && window.location.host !== destination.host;
        }

        document.body.addEventListener('click', (e) => {
            if (!e.target) return;
            const link_el = e.target.closest('a');
            if (link_el && isThirdPartyLink(link_el)) {
                // TODO: replace with custom popup
                const should_proceed = window.confirm(localize('You will be redirected to a third-party website which is not owned by Binary.com. Click OK to proceed.'));
                if (!should_proceed) e.preventDefault();
            }
        });
    };

    const beforeContentChange = () => {
        if (active_script) {
            BinarySocket.removeOnDisconnect();
            if (typeof active_script.onUnload === 'function') {
                active_script.onUnload();
            }
            active_script = null;
        }
        ScrollToAnchor.cleanup();
    };

    const afterContentChange = (e) => {
        Page.onLoad();
        GTM.pushDataLayer();

        const this_page = e.detail.getAttribute('data-page');
        if (this_page in pages_config) {
            loadHandler(pages_config[this_page]);
        } else if (/\/get-started\//i.test(window.location.pathname)) {
            loadHandler(pages_config['get-started']);
        }

        ContentVisibility.init();
        ScrollToAnchor.init();
    };

    const error_messages = {
        login       : () => localize('Please [_1]log in[_2] or [_3]sign up[_2] to view this page.', [`<a href="${'javascript:;'}">`, '', `<a href="${urlFor()}">`]),
        only_virtual: 'Sorry, this feature is available to virtual accounts only.',
        only_real   : 'This feature is not relevant to virtual-money accounts.',
    };

    const loadHandler = (config) => {
        active_script = config.module;
        if (config.is_authenticated) {
            if (!Client.isLoggedIn()) {
                displayMessage(error_messages.login());
            } else {
                BinarySocket.wait('authorize')
                    .then((response) => {
                        if (response.error) {
                            displayMessage(error_messages.login());
                        } else if (config.only_virtual && !Client.get('is_virtual')) {
                            displayMessage(error_messages.only_virtual);
                        } else if (config.only_real && Client.get('is_virtual')) {
                            displayMessage(error_messages.only_real);
                        } else {
                            loadActiveScript(config);
                        }
                    });
            }
        } else if (config.not_authenticated && Client.isLoggedIn()) {
            BinaryPjax.load(Client.defaultRedirectUrl(), true);
        } else {
            loadActiveScript(config);
        }
        BinarySocket.setOnDisconnect(active_script.onDisconnect);
    };

    const loadActiveScript = (config) => {
        if (active_script && typeof active_script.onLoad === 'function') {
            // only pages that call formatMoney should wait for website_status
            if (config.needs_currency) {
                BinarySocket.wait('website_status').then(() => {
                    active_script.onLoad();
                });
            } else {
                active_script.onLoad();
            }
        }
    };

    const displayMessage = (message) => {
        const content = container.querySelector('#content .container');
        if (!content) {
            return;
        }

        const div_container = createElement('div', { class: 'logged_out_title_container', html: content.getElementsByTagName('h1')[0] });
        const div_notice    = createElement('p', { class: 'center-text notice-msg', html: localize(message) });

        div_container.appendChild(div_notice);

        content.html(div_container);

        const link = content.getElementsByTagName('a')[0];
        if (link) {
            link.addEventListener('click', () => { Login.redirectToLogin(); });
        }
    };

    return {
        init,
    };
})();

module.exports = BinaryLoader;
<|endoftext|>


<commit_before>IonicModule

.controller('$ionicNavBar', [
  '$scope',
  '$element',
  '$attrs',
  '$compile',
  '$timeout',
  '$ionicNavBarDelegate',
  '$ionicConfig',
  '$ionicHistory',
function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $ionicConfig, $ionicHistory) {

  var CSS_HIDE = 'hide';
  var DATA_NAV_BAR_CTRL = '$ionNavBarController';
  var PRIMARY_BUTTONS = 'primaryButtons';
  var SECONDARY_BUTTONS = 'secondaryButtons';
  var BACK_BUTTON = 'backButton';
  var ITEM_TYPES = 'primaryButtons secondaryButtons leftButtons rightButtons title'.split(' ');

  var self = this;
  var headerBars = [];
  var navElementHtml = {};
  var isVisible = true;
  var queuedTransitionStart, queuedTransitionEnd, latestTransitionId;

  $element.parent().data(DATA_NAV_BAR_CTRL, self);

  var delegateHandle = $attrs.delegateHandle || 'navBar' + ionic.Utils.nextUid();

  var deregisterInstance = $ionicNavBarDelegate._registerInstance(self, delegateHandle);

  self.init = function() {
    $element.addClass('nav-bar-container');
    ionic.DomUtil.cachedAttr($element, 'nav-bar-transition', $ionicConfig.views.transition());

    // create two nav bar blocks which will trade out which one is shown
    self.createHeaderBar(false);
    self.createHeaderBar(true);

    $scope.$emit('ionNavBar.init', delegateHandle);
  };

  self.createHeaderBar = function(isActive) {
    var containerEle = jqLite('
');
    ionic.DomUtil.cachedAttr(containerEle, 'nav-bar', isActive ? 'active' : 'cached');

    var alignTitle = $attrs.alignTitle || $ionicConfig.navBar.alignTitle();
    var headerBarEle = jqLite('<ion-header-bar>').addClass($attrs['class']).attr('align-title', alignTitle);
    if (isDefined($attrs.noTapScroll)) headerBarEle.attr('no-tap-scroll', $attrs.noTapScroll);
    var titleEle = jqLite('
');
    var navEle = {};
    var lastViewItemEle = {};
    var leftButtonsEle, rightButtonsEle;

    navEle[BACK_BUTTON] = createNavElement(BACK_BUTTON);
    navEle[BACK_BUTTON] && headerBarEle.append(navEle[BACK_BUTTON]);

    // append title in the header, this is the rock to where buttons append
    headerBarEle.append(titleEle);

    forEach(ITEM_TYPES, function(itemType) {
      // create default button elements
      navEle[itemType] = createNavElement(itemType);
      // append and position buttons
      positionItem(navEle[itemType], itemType);
    });

    // add header-item to the root children
    for (var x = 0; x < headerBarEle[0].children.length; x++) {
      headerBarEle[0].children[x].classList.add('header-item');
    }

    // compile header and append to the DOM
    containerEle.append(headerBarEle);
    $element.append($compile(containerEle)($scope.$new()));

    var headerBarCtrl = headerBarEle.data('$ionHeaderBarController');
    headerBarCtrl.backButtonIcon = $ionicConfig.backButton.icon();
    headerBarCtrl.backButtonText = $ionicConfig.backButton.text();

    var headerBarInstance = {
      isActive: isActive,
      title: function(newTitleText) {
        headerBarCtrl.title(newTitleText);
      },
      setItem: function(navBarItemEle, itemType) {
        // first make sure any exiting nav bar item has been removed
        headerBarInstance.removeItem(itemType);

        if (navBarItemEle) {
          if (itemType === 'title') {
            // clear out the text based title
            headerBarInstance.title("");
          }

          // there's a custom nav bar item
          positionItem(navBarItemEle, itemType);

          if (navEle[itemType]) {
            // make sure the default on this itemType is hidden
            navEle[itemType].addClass(CSS_HIDE);
          }
          lastViewItemEle[itemType] = navBarItemEle;

        } else if (navEle[itemType]) {
          // there's a default button for this side and no view button
          navEle[itemType].removeClass(CSS_HIDE);
        }
      },
      removeItem: function(itemType) {
        if (lastViewItemEle[itemType]) {
          lastViewItemEle[itemType].scope().$destroy();
          lastViewItemEle[itemType].remove();
          lastViewItemEle[itemType] = null;
        }
      },
      containerEle: function() {
        return containerEle;
      },
      headerBarEle: function() {
        return headerBarEle;
      },
      afterLeave: function() {
        forEach(ITEM_TYPES, function(itemType) {
          headerBarInstance.removeItem(itemType);
        });
        headerBarCtrl.resetBackButton();
      },
      controller: function() {
        return headerBarCtrl;
      },
      destroy: function() {
        forEach(ITEM_TYPES, function(itemType) {
          headerBarInstance.removeItem(itemType);
        });
        containerEle.scope().$destroy();
        for (var n in navEle) {
          if (navEle[n]) {
            navEle[n].removeData();
            navEle[n] = null;
          }
        }
        leftButtonsEle && leftButtonsEle.removeData();
        rightButtonsEle && rightButtonsEle.removeData();
        titleEle.removeData();
        headerBarEle.removeData();
        containerEle.remove();
        containerEle = headerBarEle = titleEle = leftButtonsEle = rightButtonsEle = null;
      }
    };

    function positionItem(ele, itemType) {
      if (!ele) return;

      if (itemType === 'title') {
        // title element
        titleEle.append(ele);

      } else if (itemType == 'rightButtons' ||
                (itemType == SECONDARY_BUTTONS && $ionicConfig.navBar.positionSecondaryButtons() != 'left') ||
                (itemType == PRIMARY_BUTTONS && $ionicConfig.navBar.positionPrimaryButtons() == 'right')) {
        // right side
        if (!rightButtonsEle) {
          rightButtonsEle = jqLite('
');
          headerBarEle.append(rightButtonsEle);
        }
        if (itemType == SECONDARY_BUTTONS) {
          rightButtonsEle.append(ele);
        } else {
          rightButtonsEle.prepend(ele);
        }

      } else {
        // left side
        if (!leftButtonsEle) {
          leftButtonsEle = jqLite('
');
          if (navEle[BACK_BUTTON]) {
            navEle[BACK_BUTTON].after(leftButtonsEle);
          } else {
            headerBarEle.prepend(leftButtonsEle);
          }
        }
        if (itemType == SECONDARY_BUTTONS) {
          leftButtonsEle.append(ele);
        } else {
          leftButtonsEle.prepend(ele);
        }
      }

    }

    headerBars.push(headerBarInstance);

    return headerBarInstance;
  };

  self.navElement = function(type, html) {
    if (isDefined(html)) {
      navElementHtml[type] = html;
    }
    return navElementHtml[type];
  };

  self.update = function(viewData) {
    var showNavBar = !viewData.hasHeaderBar && viewData.showNavBar;
    viewData.transition = $ionicConfig.views.transition();

    if (!showNavBar) {
      viewData.direction = 'none';
    }

    self.enable(showNavBar);
    var enteringHeaderBar = self.isInitialized ? getOffScreenHeaderBar() : getOnScreenHeaderBar();
    var leavingHeaderBar = self.isInitialized ? getOnScreenHeaderBar() : null;
    var enteringHeaderCtrl = enteringHeaderBar.controller();

    // update if the entering header should show the back button or not
    enteringHeaderCtrl.enableBack(viewData.enableBack, true);
    enteringHeaderCtrl.showBack(viewData.showBack, true);
    enteringHeaderCtrl.updateBackButton();

    // update the entering header bar's title
    self.title(viewData.title, enteringHeaderBar);

    self.showBar(showNavBar);

    // update the nav bar items, depending if the view has their own or not
    if (viewData.navBarItems) {
      forEach(ITEM_TYPES, function(itemType) {
        enteringHeaderBar.setItem(viewData.navBarItems[itemType], itemType);
      });
    }

    // begin transition of entering and leaving header bars
    self.transition(enteringHeaderBar, leavingHeaderBar, viewData);

    self.isInitialized = true;
    navSwipeAttr('');
  };

  self.transition = function(enteringHeaderBar, leavingHeaderBar, viewData) {
    var enteringHeaderBarCtrl = enteringHeaderBar.controller();
    var transitionFn = $ionicConfig.transitions.navBar[viewData.navBarTransition] || $ionicConfig.transitions.navBar.none;
    var transitionId = viewData.transitionId;

    enteringHeaderBarCtrl.beforeEnter(viewData);

    var navBarTransition = transitionFn(enteringHeaderBar, leavingHeaderBar, viewData.direction, viewData.shouldAnimate && self.isInitialized);

    ionic.DomUtil.cachedAttr($element, 'nav-bar-transition', viewData.navBarTransition);
    ionic.DomUtil.cachedAttr($element, 'nav-bar-direction', viewData.direction);

    if (navBarTransition.shouldAnimate && viewData.renderEnd) {
      navBarAttr(enteringHeaderBar, 'stage');
    } else {
      navBarAttr(enteringHeaderBar, 'entering');
      navBarAttr(leavingHeaderBar, 'leaving');
    }

    enteringHeaderBarCtrl.resetBackButton(viewData);

    navBarTransition.run(0);

    self.activeTransition = {
      run: function(step) {
        navBarTransition.shouldAnimate = false;
        navBarTransition.direction = 'back';
        navBarTransition.run(step);
      },
      cancel: function(shouldAnimate, speed, cancelData) {
        navSwipeAttr(speed);
        navBarAttr(leavingHeaderBar, 'active');
        navBarAttr(enteringHeaderBar, 'cached');
        navBarTransition.shouldAnimate = shouldAnimate;
        navBarTransition.run(0);
        self.activeTransition = navBarTransition = null;

        var runApply;
        if (cancelData.showBar !== self.showBar()) {
          self.showBar(cancelData.showBar);
        }
        if (cancelData.showBackButton !== self.showBackButton()) {
          self.showBackButton(cancelData.showBackButton);
        }
        if (runApply) {
          $scope.$apply();
        }
      },
      complete: function(shouldAnimate, speed) {
        navSwipeAttr(speed);
        navBarTransition.shouldAnimate = shouldAnimate;
        navBarTransition.run(1);
        queuedTransitionEnd = transitionEnd;
      }
    };

    $timeout(enteringHeaderBarCtrl.align, 16);

    queuedTransitionStart = function() {
      if (latestTransitionId !== transitionId) return;

      navBarAttr(enteringHeaderBar, 'entering');
      navBarAttr(leavingHeaderBar, 'leaving');

      navBarTransition.run(1);

      queuedTransitionEnd = function() {
        if (latestTransitionId == transitionId || !navBarTransition.shouldAnimate) {
          transitionEnd();
        }
      };

      queuedTransitionStart = null;
    };

    function transitionEnd() {
      for (var x = 0; x < headerBars.length; x++) {
        headerBars[x].isActive = false;
      }
      enteringHeaderBar.isActive = true;

      navBarAttr(enteringHeaderBar, 'active');
      navBarAttr(leavingHeaderBar, 'cached');

      self.activeTransition = navBarTransition = queuedTransitionEnd = null;
    }

    queuedTransitionStart();
  };

  self.triggerTransitionStart = function(triggerTransitionId) {
    latestTransitionId = triggerTransitionId;
    queuedTransitionStart && queuedTransitionStart();
  };

  self.triggerTransitionEnd = function() {
    queuedTransitionEnd && queuedTransitionEnd();
  };

  self.showBar = function(shouldShow) {
    if (arguments.length) {
      self.visibleBar(shouldShow);
      $scope.$parent.$hasHeader = !!shouldShow;
    }
    return !!$scope.$parent.$hasHeader;
  };

  self.visibleBar = function(shouldShow) {
    if (shouldShow && !isVisible) {
      $element.removeClass(CSS_HIDE);
      self.align();
    } else if (!shouldShow && isVisible) {
      $element.addClass(CSS_HIDE);
    }
    isVisible = shouldShow;
  };

  self.enable = function(val) {
    // set primary to show first
    self.visibleBar(val);

    // set non primary to hide second
    for (var x = 0; x < $ionicNavBarDelegate._instances.length; x++) {
      if ($ionicNavBarDelegate._instances[x] !== self) $ionicNavBarDelegate._instances[x].visibleBar(false);
    }
  };

  /**
   * @ngdoc method
   * @name $ionicNavBar#showBackButton
   * @description Show/hide the nav bar back button when there is a
   * back view. If the back button is not possible, for example, the
   * first view in the stack, then this will not force the back button
   * to show.
   */
  self.showBackButton = function(shouldShow) {
    if (arguments.length) {
      for (var x = 0; x < headerBars.length; x++) {
        headerBars[x].controller().showNavBack(!!shouldShow);
      }
      $scope.$isBackButtonShown = !!shouldShow;
    }
    return $scope.$isBackButtonShown;
  };

  /**
   * @ngdoc method
   * @name $ionicNavBar#showActiveBackButton
   * @description Show/hide only the active header bar's back button.
   */
  self.showActiveBackButton = function(shouldShow) {
    var headerBar = getOnScreenHeaderBar();
    if (headerBar) {
      if (arguments.length) {
        return headerBar.controller().showBack(shouldShow);
      }
      return headerBar.controller().showBack();
    }
  };

  self.title = function(newTitleText, headerBar) {
    if (isDefined(newTitleText)) {
      newTitleText = newTitleText || '';
      headerBar = headerBar || getOnScreenHeaderBar();
      headerBar && headerBar.title(newTitleText);
      $scope.$title = newTitleText;
      $ionicHistory.currentTitle(newTitleText);
    }
    return $scope.$title;
  };

  self.align = function(val, headerBar) {
    headerBar = headerBar || getOnScreenHeaderBar();
    headerBar && headerBar.controller().align(val);
  };

  self.hasTabsTop = function(isTabsTop) {
    $element[isTabsTop ? 'addClass' : 'removeClass']('nav-bar-tabs-top');
  };

  self.hasBarSubheader = function(isBarSubheader) {
    $element[isBarSubheader ? 'addClass' : 'removeClass']('nav-bar-has-subheader');
  };

  // DEPRECATED, as of v1.0.0-beta14 -------
  self.changeTitle = function(val) {
    deprecatedWarning('changeTitle(val)', 'title(val)');
    self.title(val);
  };
  self.setTitle = function(val) {
    deprecatedWarning('setTitle(val)', 'title(val)');
    self.title(val);
  };
  self.getTitle = function() {
    deprecatedWarning('getTitle()', 'title()');
    return self.title();
  };
  self.back = function() {
    deprecatedWarning('back()', '$ionicHistory.goBack()');
    $ionicHistory.goBack();
  };
  self.getPreviousTitle = function() {
    deprecatedWarning('getPreviousTitle()', '$ionicHistory.backTitle()');
    $ionicHistory.goBack();
  };
  function deprecatedWarning(oldMethod, newMethod) {
    var warn = console.warn || console.log;
    warn && warn.call(console, 'navBarController.' + oldMethod + ' is deprecated, please use ' + newMethod + ' instead');
  }
  // END DEPRECATED -------

  function createNavElement(type) {
    if (navElementHtml[type]) {
      return jqLite(navElementHtml[type]);
    }
  }

  function getOnScreenHeaderBar() {
    for (var x = 0; x < headerBars.length; x++) {
      if (headerBars[x].isActive) return headerBars[x];
    }
  }

  function getOffScreenHeaderBar() {
    for (var x = 0; x < headerBars.length; x++) {
      if (!headerBars[x].isActive) return headerBars[x];
    }
  }

  function navBarAttr(ctrl, val) {
    ctrl && ionic.DomUtil.cachedAttr(ctrl.containerEle(), 'nav-bar', val);
  }

  function navSwipeAttr(val) {
    ionic.DomUtil.cachedAttr($element, 'nav-swipe', val);
  }

  $scope.$on('$destroy', function() {
    $scope.$parent.$hasHeader = false;
    $element.parent().removeData(DATA_NAV_BAR_CTRL);
    for (var x = 0; x < headerBars.length; x++) {
      headerBars[x].destroy();
    }
    $element.remove();
    $element = headerBars = null;
    deregisterInstance();
  });

}]);
<commit_msg>fix: "clean" the leaving header bar when hidden (so no invisible nav-buttons can be clicked in the next view)<commit_after>IonicModule

.controller('$ionicNavBar', [
  '$scope',
  '$element',
  '$attrs',
  '$compile',
  '$timeout',
  '$ionicNavBarDelegate',
  '$ionicConfig',
  '$ionicHistory',
function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $ionicConfig, $ionicHistory) {

  var CSS_HIDE = 'hide';
  var DATA_NAV_BAR_CTRL = '$ionNavBarController';
  var PRIMARY_BUTTONS = 'primaryButtons';
  var SECONDARY_BUTTONS = 'secondaryButtons';
  var BACK_BUTTON = 'backButton';
  var ITEM_TYPES = 'primaryButtons secondaryButtons leftButtons rightButtons title'.split(' ');

  var self = this;
  var headerBars = [];
  var navElementHtml = {};
  var isVisible = true;
  var queuedTransitionStart, queuedTransitionEnd, latestTransitionId;

  $element.parent().data(DATA_NAV_BAR_CTRL, self);

  var delegateHandle = $attrs.delegateHandle || 'navBar' + ionic.Utils.nextUid();

  var deregisterInstance = $ionicNavBarDelegate._registerInstance(self, delegateHandle);

  self.init = function() {
    $element.addClass('nav-bar-container');
    ionic.DomUtil.cachedAttr($element, 'nav-bar-transition', $ionicConfig.views.transition());

    // create two nav bar blocks which will trade out which one is shown
    self.createHeaderBar(false);
    self.createHeaderBar(true);

    $scope.$emit('ionNavBar.init', delegateHandle);
  };

  self.createHeaderBar = function(isActive) {
    var containerEle = jqLite('
');
    ionic.DomUtil.cachedAttr(containerEle, 'nav-bar', isActive ? 'active' : 'cached');

    var alignTitle = $attrs.alignTitle || $ionicConfig.navBar.alignTitle();
    var headerBarEle = jqLite('<ion-header-bar>').addClass($attrs['class']).attr('align-title', alignTitle);
    if (isDefined($attrs.noTapScroll)) headerBarEle.attr('no-tap-scroll', $attrs.noTapScroll);
    var titleEle = jqLite('
');
    var navEle = {};
    var lastViewItemEle = {};
    var leftButtonsEle, rightButtonsEle;

    navEle[BACK_BUTTON] = createNavElement(BACK_BUTTON);
    navEle[BACK_BUTTON] && headerBarEle.append(navEle[BACK_BUTTON]);

    // append title in the header, this is the rock to where buttons append
    headerBarEle.append(titleEle);

    forEach(ITEM_TYPES, function(itemType) {
      // create default button elements
      navEle[itemType] = createNavElement(itemType);
      // append and position buttons
      positionItem(navEle[itemType], itemType);
    });

    // add header-item to the root children
    for (var x = 0; x < headerBarEle[0].children.length; x++) {
      headerBarEle[0].children[x].classList.add('header-item');
    }

    // compile header and append to the DOM
    containerEle.append(headerBarEle);
    $element.append($compile(containerEle)($scope.$new()));

    var headerBarCtrl = headerBarEle.data('$ionHeaderBarController');
    headerBarCtrl.backButtonIcon = $ionicConfig.backButton.icon();
    headerBarCtrl.backButtonText = $ionicConfig.backButton.text();

    var headerBarInstance = {
      isActive: isActive,
      title: function(newTitleText) {
        headerBarCtrl.title(newTitleText);
      },
      setItem: function(navBarItemEle, itemType) {
        // first make sure any exiting nav bar item has been removed
        headerBarInstance.removeItem(itemType);

        if (navBarItemEle) {
          if (itemType === 'title') {
            // clear out the text based title
            headerBarInstance.title("");
          }

          // there's a custom nav bar item
          positionItem(navBarItemEle, itemType);

          if (navEle[itemType]) {
            // make sure the default on this itemType is hidden
            navEle[itemType].addClass(CSS_HIDE);
          }
          lastViewItemEle[itemType] = navBarItemEle;

        } else if (navEle[itemType]) {
          // there's a default button for this side and no view button
          navEle[itemType].removeClass(CSS_HIDE);
        }
      },
      removeItem: function(itemType) {
        if (lastViewItemEle[itemType]) {
          lastViewItemEle[itemType].scope().$destroy();
          lastViewItemEle[itemType].remove();
          lastViewItemEle[itemType] = null;
        }
      },
      containerEle: function() {
        return containerEle;
      },
      headerBarEle: function() {
        return headerBarEle;
      },
      afterLeave: function() {
        forEach(ITEM_TYPES, function(itemType) {
          headerBarInstance.removeItem(itemType);
        });
        headerBarCtrl.resetBackButton();
      },
      controller: function() {
        return headerBarCtrl;
      },
      destroy: function() {
        forEach(ITEM_TYPES, function(itemType) {
          headerBarInstance.removeItem(itemType);
        });
        containerEle.scope().$destroy();
        for (var n in navEle) {
          if (navEle[n]) {
            navEle[n].removeData();
            navEle[n] = null;
          }
        }
        leftButtonsEle && leftButtonsEle.removeData();
        rightButtonsEle && rightButtonsEle.removeData();
        titleEle.removeData();
        headerBarEle.removeData();
        containerEle.remove();
        containerEle = headerBarEle = titleEle = leftButtonsEle = rightButtonsEle = null;
      }
    };

    function positionItem(ele, itemType) {
      if (!ele) return;

      if (itemType === 'title') {
        // title element
        titleEle.append(ele);

      } else if (itemType == 'rightButtons' ||
                (itemType == SECONDARY_BUTTONS && $ionicConfig.navBar.positionSecondaryButtons() != 'left') ||
                (itemType == PRIMARY_BUTTONS && $ionicConfig.navBar.positionPrimaryButtons() == 'right')) {
        // right side
        if (!rightButtonsEle) {
          rightButtonsEle = jqLite('
');
          headerBarEle.append(rightButtonsEle);
        }
        if (itemType == SECONDARY_BUTTONS) {
          rightButtonsEle.append(ele);
        } else {
          rightButtonsEle.prepend(ele);
        }

      } else {
        // left side
        if (!leftButtonsEle) {
          leftButtonsEle = jqLite('
');
          if (navEle[BACK_BUTTON]) {
            navEle[BACK_BUTTON].after(leftButtonsEle);
          } else {
            headerBarEle.prepend(leftButtonsEle);
          }
        }
        if (itemType == SECONDARY_BUTTONS) {
          leftButtonsEle.append(ele);
        } else {
          leftButtonsEle.prepend(ele);
        }
      }

    }

    headerBars.push(headerBarInstance);

    return headerBarInstance;
  };

  self.navElement = function(type, html) {
    if (isDefined(html)) {
      navElementHtml[type] = html;
    }
    return navElementHtml[type];
  };

  self.update = function(viewData) {
    var showNavBar = !viewData.hasHeaderBar && viewData.showNavBar;
    viewData.transition = $ionicConfig.views.transition();

    if (!showNavBar) {
      viewData.direction = 'none';
    }

    self.enable(showNavBar);
    var enteringHeaderBar = self.isInitialized ? getOffScreenHeaderBar() : getOnScreenHeaderBar();
    var leavingHeaderBar = self.isInitialized ? getOnScreenHeaderBar() : null;
    var enteringHeaderCtrl = enteringHeaderBar.controller();

    // update if the entering header should show the back button or not
    enteringHeaderCtrl.enableBack(viewData.enableBack, true);
    enteringHeaderCtrl.showBack(viewData.showBack, true);
    enteringHeaderCtrl.updateBackButton();

    // update the entering header bar's title
    self.title(viewData.title, enteringHeaderBar);

    self.showBar(showNavBar);

    // update the nav bar items, depending if the view has their own or not
    if (viewData.navBarItems) {
      forEach(ITEM_TYPES, function(itemType) {
        enteringHeaderBar.setItem(viewData.navBarItems[itemType], itemType);
      });
    }

    // begin transition of entering and leaving header bars
    self.transition(enteringHeaderBar, leavingHeaderBar, viewData);

    self.isInitialized = true;
    navSwipeAttr('');
  };

  self.transition = function(enteringHeaderBar, leavingHeaderBar, viewData) {
    var enteringHeaderBarCtrl = enteringHeaderBar.controller();
    var transitionFn = $ionicConfig.transitions.navBar[viewData.navBarTransition] || $ionicConfig.transitions.navBar.none;
    var transitionId = viewData.transitionId;

    enteringHeaderBarCtrl.beforeEnter(viewData);

    var navBarTransition = transitionFn(enteringHeaderBar, leavingHeaderBar, viewData.direction, viewData.shouldAnimate && self.isInitialized);

    ionic.DomUtil.cachedAttr($element, 'nav-bar-transition', viewData.navBarTransition);
    ionic.DomUtil.cachedAttr($element, 'nav-bar-direction', viewData.direction);

    if (navBarTransition.shouldAnimate && viewData.renderEnd) {
      navBarAttr(enteringHeaderBar, 'stage');
    } else {
      navBarAttr(enteringHeaderBar, 'entering');
      navBarAttr(leavingHeaderBar, 'leaving');
    }

    enteringHeaderBarCtrl.resetBackButton(viewData);

    navBarTransition.run(0);

    self.activeTransition = {
      run: function(step) {
        navBarTransition.shouldAnimate = false;
        navBarTransition.direction = 'back';
        navBarTransition.run(step);
      },
      cancel: function(shouldAnimate, speed, cancelData) {
        navSwipeAttr(speed);
        navBarAttr(leavingHeaderBar, 'active');
        navBarAttr(enteringHeaderBar, 'cached');
        navBarTransition.shouldAnimate = shouldAnimate;
        navBarTransition.run(0);
        self.activeTransition = navBarTransition = null;

        var runApply;
        if (cancelData.showBar !== self.showBar()) {
          self.showBar(cancelData.showBar);
        }
        if (cancelData.showBackButton !== self.showBackButton()) {
          self.showBackButton(cancelData.showBackButton);
        }
        if (runApply) {
          $scope.$apply();
        }
      },
      complete: function(shouldAnimate, speed) {
        navSwipeAttr(speed);
        navBarTransition.shouldAnimate = shouldAnimate;
        navBarTransition.run(1);
        queuedTransitionEnd = transitionEnd;
      }
    };

    $timeout(enteringHeaderBarCtrl.align, 16);

    queuedTransitionStart = function() {
      if (latestTransitionId !== transitionId) return;

      navBarAttr(enteringHeaderBar, 'entering');
      navBarAttr(leavingHeaderBar, 'leaving');

      navBarTransition.run(1);

      queuedTransitionEnd = function() {
        if (leavingHeaderBar) {
          leavingHeaderBar.afterLeave();
        }

        if (latestTransitionId == transitionId || !navBarTransition.shouldAnimate) {
          transitionEnd();
        }
      };

      queuedTransitionStart = null;
    };

    function transitionEnd() {
      for (var x = 0; x < headerBars.length; x++) {
        headerBars[x].isActive = false;
      }
      enteringHeaderBar.isActive = true;

      navBarAttr(enteringHeaderBar, 'active');
      navBarAttr(leavingHeaderBar, 'cached');

      self.activeTransition = navBarTransition = queuedTransitionEnd = null;
    }

    queuedTransitionStart();
  };

  self.triggerTransitionStart = function(triggerTransitionId) {
    latestTransitionId = triggerTransitionId;
    queuedTransitionStart && queuedTransitionStart();
  };

  self.triggerTransitionEnd = function() {
    queuedTransitionEnd && queuedTransitionEnd();
  };

  self.showBar = function(shouldShow) {
    if (arguments.length) {
      self.visibleBar(shouldShow);
      $scope.$parent.$hasHeader = !!shouldShow;
    }
    return !!$scope.$parent.$hasHeader;
  };

  self.visibleBar = function(shouldShow) {
    if (shouldShow && !isVisible) {
      $element.removeClass(CSS_HIDE);
      self.align();
    } else if (!shouldShow && isVisible) {
      $element.addClass(CSS_HIDE);
    }
    isVisible = shouldShow;
  };

  self.enable = function(val) {
    // set primary to show first
    self.visibleBar(val);

    // set non primary to hide second
    for (var x = 0; x < $ionicNavBarDelegate._instances.length; x++) {
      if ($ionicNavBarDelegate._instances[x] !== self) $ionicNavBarDelegate._instances[x].visibleBar(false);
    }
  };

  /**
   * @ngdoc method
   * @name $ionicNavBar#showBackButton
   * @description Show/hide the nav bar back button when there is a
   * back view. If the back button is not possible, for example, the
   * first view in the stack, then this will not force the back button
   * to show.
   */
  self.showBackButton = function(shouldShow) {
    if (arguments.length) {
      for (var x = 0; x < headerBars.length; x++) {
        headerBars[x].controller().showNavBack(!!shouldShow);
      }
      $scope.$isBackButtonShown = !!shouldShow;
    }
    return $scope.$isBackButtonShown;
  };

  /**
   * @ngdoc method
   * @name $ionicNavBar#showActiveBackButton
   * @description Show/hide only the active header bar's back button.
   */
  self.showActiveBackButton = function(shouldShow) {
    var headerBar = getOnScreenHeaderBar();
    if (headerBar) {
      if (arguments.length) {
        return headerBar.controller().showBack(shouldShow);
      }
      return headerBar.controller().showBack();
    }
  };

  self.title = function(newTitleText, headerBar) {
    if (isDefined(newTitleText)) {
      newTitleText = newTitleText || '';
      headerBar = headerBar || getOnScreenHeaderBar();
      headerBar && headerBar.title(newTitleText);
      $scope.$title = newTitleText;
      $ionicHistory.currentTitle(newTitleText);
    }
    return $scope.$title;
  };

  self.align = function(val, headerBar) {
    headerBar = headerBar || getOnScreenHeaderBar();
    headerBar && headerBar.controller().align(val);
  };

  self.hasTabsTop = function(isTabsTop) {
    $element[isTabsTop ? 'addClass' : 'removeClass']('nav-bar-tabs-top');
  };

  self.hasBarSubheader = function(isBarSubheader) {
    $element[isBarSubheader ? 'addClass' : 'removeClass']('nav-bar-has-subheader');
  };

  // DEPRECATED, as of v1.0.0-beta14 -------
  self.changeTitle = function(val) {
    deprecatedWarning('changeTitle(val)', 'title(val)');
    self.title(val);
  };
  self.setTitle = function(val) {
    deprecatedWarning('setTitle(val)', 'title(val)');
    self.title(val);
  };
  self.getTitle = function() {
    deprecatedWarning('getTitle()', 'title()');
    return self.title();
  };
  self.back = function() {
    deprecatedWarning('back()', '$ionicHistory.goBack()');
    $ionicHistory.goBack();
  };
  self.getPreviousTitle = function() {
    deprecatedWarning('getPreviousTitle()', '$ionicHistory.backTitle()');
    $ionicHistory.goBack();
  };
  function deprecatedWarning(oldMethod, newMethod) {
    var warn = console.warn || console.log;
    warn && warn.call(console, 'navBarController.' + oldMethod + ' is deprecated, please use ' + newMethod + ' instead');
  }
  // END DEPRECATED -------

  function createNavElement(type) {
    if (navElementHtml[type]) {
      return jqLite(navElementHtml[type]);
    }
  }

  function getOnScreenHeaderBar() {
    for (var x = 0; x < headerBars.length; x++) {
      if (headerBars[x].isActive) return headerBars[x];
    }
  }

  function getOffScreenHeaderBar() {
    for (var x = 0; x < headerBars.length; x++) {
      if (!headerBars[x].isActive) return headerBars[x];
    }
  }

  function navBarAttr(ctrl, val) {
    ctrl && ionic.DomUtil.cachedAttr(ctrl.containerEle(), 'nav-bar', val);
  }

  function navSwipeAttr(val) {
    ionic.DomUtil.cachedAttr($element, 'nav-swipe', val);
  }

  $scope.$on('$destroy', function() {
    $scope.$parent.$hasHeader = false;
    $element.parent().removeData(DATA_NAV_BAR_CTRL);
    for (var x = 0; x < headerBars.length; x++) {
      headerBars[x].destroy();
    }
    $element.remove();
    $element = headerBars = null;
    deregisterInstance();
  });

}]);
<|endoftext|>