<commit_before>var nanofeed = (function () {
  'use strict';

  if (typeof Object.assign !== 'function') {
    Object.assign = function (a, b) {
      for (var key in b) {
        if (b.hasOwnProperty(key)) {
          a[key] = b[key];
        }
      }
      return a;
    };
  }

  // We don't want extra columns carrying unnecessary data through the network
  function getQueryColumns(options) {
    var cols = '';

    cols += options.title ? 'item.title,' : '';
    cols += options.link ? 'item.link,' : '';
    cols += options.date ? 'item.pubDate,' : '';
    cols += options.description ? 'item.description,' : '';

    return cols.slice(0, -1) || 'item.title';
  }

  function getJSON(url, callback) {
    var request = new XMLHttpRequest();
    request.onload = function () {
      var data;
      try {
        data = JSON.parse(request.responseText);
      }
      catch (e) {
      }
      callback(data);
    };
    request.open('GET', url, true);
    request.send();
  }

  return {
    fetch: function (urls, options, callback) {
      if (typeof urls === 'string') {
        urls = [urls];
      }
      else if (!Array.isArray(urls) || !urls.length) {
        return this;
      }

      if (typeof options === 'function') {
        callback = options;
        options = {};
      }

      options = Object.assign({
        title: true,
        link: true,
        date: false,
        description: false,
        qty: 5
      }, options);

      // Optimized cross-product make simple array of results in 'query.results.results.item'
      // All tables Env allows select from 'query.multi'
      var config = {
        baseUrl: '//query.yahooapis.com/v1/public/yql?format=json&callback=&' +
        'crossProduct=optimized&env=http://datatables.org/alltables.env',
        template: 'SELECT {COLS} FROM query.multi WHERE queries=\'' +
        'SELECT title,link,pubDate,description ' +
        'FROM rss ' +
        'WHERE url in ("{URLS}")' +
        '|UNIQUE(field="title",hideRepeatCount="true")' +
        '|UNIQUE(field="link",hideRepeatCount="true")' +
        '|SORT(field="pubDate",descending="true")' +
        '|TRUNCATE({QTY})\''
      };

      var query = config.template
        .replace('{COLS}', getQueryColumns(options))
        .replace('{URLS}', urls.join('","'))
        .replace('{QTY}', options.qty);

      var url = config.baseUrl + '&q=' + encodeURIComponent(query);

      getJSON(url, function (json) {
        if (json && json.query && json.query.count >= 0) {
          var data = [];
          if (json.query.count) {
            try {
              var result = json.query.results.results.item;
              data = result.length ? result : [result];

              if (options.date) {
                data.forEach(function (item) {
                  item.pubDate = new Date(item.pubDate);
                });
              }
            }
            catch (e) {
            }
          }
          return callback(data);
        }
      });

      return this;
    }
  }
})();
<commit_msg>extend default options with custom only when late is provided<commit_after>var nanofeed = (function () {
  'use strict';

  if (typeof Object.assign !== 'function') {
    Object.assign = function (a, b) {
      for (var key in b) {
        if (b.hasOwnProperty(key)) {
          a[key] = b[key];
        }
      }
      return a;
    };
  }

  // We don't want extra columns carrying unnecessary data through the network
  function getQueryColumns(options) {
    var cols = '';

    cols += options.title ? 'item.title,' : '';
    cols += options.link ? 'item.link,' : '';
    cols += options.date ? 'item.pubDate,' : '';
    cols += options.description ? 'item.description,' : '';

    return cols.slice(0, -1) || 'item.title';
  }

  function getJSON(url, callback) {
    var request = new XMLHttpRequest();
    request.onload = function () {
      var data;
      try {
        data = JSON.parse(request.responseText);
      }
      catch (e) {
      }
      callback(data);
    };
    request.open('GET', url, true);
    request.send();
  }

  return {
    fetch: function (urls, options, callback) {
      if (typeof urls === 'string') {
        urls = [urls];
      }
      else if (!Array.isArray(urls) || !urls.length) {
        return this;
      }

      if (typeof options === 'function') {
        callback = options;
        options = {};
      }
      else {
        options = Object.assign({
          title: true,
          link: true,
          date: false,
          description: false,
          qty: 5
        }, options);
      }

      // Optimized cross-product make simple array of results in 'query.results.results.item'
      // All tables Env allows select from 'query.multi'
      var config = {
        baseUrl: '//query.yahooapis.com/v1/public/yql?format=json&callback=&' +
        'crossProduct=optimized&env=http://datatables.org/alltables.env',
        template: 'SELECT {COLS} FROM query.multi WHERE queries=\'' +
        'SELECT title,link,pubDate,description ' +
        'FROM rss ' +
        'WHERE url in ("{URLS}")' +
        '|UNIQUE(field="title",hideRepeatCount="true")' +
        '|UNIQUE(field="link",hideRepeatCount="true")' +
        '|SORT(field="pubDate",descending="true")' +
        '|TRUNCATE({QTY})\''
      };

      var query = config.template
        .replace('{COLS}', getQueryColumns(options))
        .replace('{URLS}', urls.join('","'))
        .replace('{QTY}', options.qty);

      var url = config.baseUrl + '&q=' + encodeURIComponent(query);

      getJSON(url, function (json) {
        if (json && json.query && json.query.count >= 0) {
          var data = [];
          if (json.query.count) {
            try {
              var result = json.query.results.results.item;
              data = result.length ? result : [result];

              if (options.date) {
                data.forEach(function (item) {
                  item.pubDate = new Date(item.pubDate);
                });
              }
            }
            catch (e) {
            }
          }
          return callback(data);
        }
      });

      return this;
    }
  }
})();
<|endoftext|>


<commit_before>var nanofeed = (function () {
  'use strict';

  var defaultOptions = {
    fields: ['title', 'link'],
    qty: 5
  };

  // We don't want extra columns carrying unnecessary data through the network
  function getQueryColumns(fields) {
    var cols = '';

    if (fields.indexOf) {
      cols += fields.indexOf('title') > -1 ? 'item.title,' : '';
      cols += fields.indexOf('link') > -1 ? 'item.link,' : '';
      cols += fields.indexOf('date') > -1 ? 'item.pubDate,' : '';
      cols += fields.indexOf('description') > -1 ? 'item.description,' : '';
    }

    return cols.slice(0, -1) || defaultOptions.fields;
  }

  function getJSON(url, callback) {
    var request = new XMLHttpRequest();
    request.onload = function () {
      var data;
      try {
        data = JSON.parse(request.responseText);
      }
      catch (e) {
      }
      callback(data);
    };
    request.open('GET', url, true);
    request.send();
  }

  return {
    fetch: function (urls, options, callback) {
      if (typeof urls === 'string') {
        urls = [urls];
      }
      else if (!Array.isArray(urls) || !urls.length) {
        return this;
      }

      if (typeof options === 'function') {
        callback = options;
        options = {};
      }

      if (!Array.isArray(options.fields)) {
        options.fields = defaultOptions.fields;
      }
      options.qty = isNaN(options.qty) ? defaultOptions.qty : parseInt(options.qty);

      // Optimized cross-product make simple array of results in 'query.results.results.item'
      // All tables Env allows select from 'query.multi'
      var config = {
        baseUrl: '//query.yahooapis.com/v1/public/yql?format=json&callback=&' +
        'crossProduct=optimized&env=http://datatables.org/alltables.env',
        template: 'SELECT {COLS} FROM query.multi WHERE queries=\'' +
        'SELECT title,link,pubDate,description ' +
        'FROM rss ' +
        'WHERE url in ("{URLS}")' +
        '|UNIQUE(field="title",hideRepeatCount="true")' +
        '|UNIQUE(field="link",hideRepeatCount="true")' +
        '|SORT(field="pubDate",descending="true")' +
        '|TRUNCATE({QTY})\''
      };

      var query = config.template
        .replace('{COLS}', getQueryColumns(options.fields))
        .replace('{URLS}', urls.join('","'))
        .replace('{QTY}', options.qty);

      var url = config.baseUrl + '&q=' + encodeURIComponent(query);

      getJSON(url, function (json) {
        if (json && json.query && json.query.count >= 0) {
          var data = [];
          if (json.query.count) {
            try {
              var result = json.query.results.results.item;
              data = result.length ? result : [result];

              if (options.fields.indexOf('date') > -1) {
                data.forEach(function (item) {
                  item.pubDate = new Date(item.pubDate);
                });
              }
            }
            catch (e) {
            }
          }
          return callback(data);
        }
      });

      return this;
    }
  }
})();
<commit_msg>moved invocation of function to inside parens<commit_after>var nanofeed = (function () {
  'use strict';

  var defaultOptions = {
    fields: ['title', 'link'],
    qty: 5
  };

  // We don't want extra columns carrying unnecessary data through the network
  function getQueryColumns(fields) {
    var cols = '';

    if (fields.indexOf) {
      cols += fields.indexOf('title') > -1 ? 'item.title,' : '';
      cols += fields.indexOf('link') > -1 ? 'item.link,' : '';
      cols += fields.indexOf('date') > -1 ? 'item.pubDate,' : '';
      cols += fields.indexOf('description') > -1 ? 'item.description,' : '';
    }

    return cols.slice(0, -1) || defaultOptions.fields;
  }

  function getJSON(url, callback) {
    var request = new XMLHttpRequest();
    request.onload = function () {
      var data;
      try {
        data = JSON.parse(request.responseText);
      }
      catch (e) {
      }
      callback(data);
    };
    request.open('GET', url, true);
    request.send();
  }

  return {
    fetch: function (urls, options, callback) {
      if (typeof urls === 'string') {
        urls = [urls];
      }
      else if (!Array.isArray(urls) || !urls.length) {
        return this;
      }

      if (typeof options === 'function') {
        callback = options;
        options = {};
      }

      if (!Array.isArray(options.fields)) {
        options.fields = defaultOptions.fields;
      }
      options.qty = isNaN(options.qty) ? defaultOptions.qty : parseInt(options.qty);

      // Optimized cross-product make simple array of results in 'query.results.results.item'
      // All tables Env allows select from 'query.multi'
      var config = {
        baseUrl: '//query.yahooapis.com/v1/public/yql?format=json&callback=&' +
        'crossProduct=optimized&env=http://datatables.org/alltables.env',
        template: 'SELECT {COLS} FROM query.multi WHERE queries=\'' +
        'SELECT title,link,pubDate,description ' +
        'FROM rss ' +
        'WHERE url in ("{URLS}")' +
        '|UNIQUE(field="title",hideRepeatCount="true")' +
        '|UNIQUE(field="link",hideRepeatCount="true")' +
        '|SORT(field="pubDate",descending="true")' +
        '|TRUNCATE({QTY})\''
      };

      var query = config.template
        .replace('{COLS}', getQueryColumns(options.fields))
        .replace('{URLS}', urls.join('","'))
        .replace('{QTY}', options.qty);

      var url = config.baseUrl + '&q=' + encodeURIComponent(query);

      getJSON(url, function (json) {
        if (json && json.query && json.query.count >= 0) {
          var data = [];
          if (json.query.count) {
            try {
              var result = json.query.results.results.item;
              data = result.length ? result : [result];

              if (options.fields.indexOf('date') > -1) {
                data.forEach(function (item) {
                  item.pubDate = new Date(item.pubDate);
                });
              }
            }
            catch (e) {
            }
          }
          return callback(data);
        }
      });

      return this;
    }
  }
}());
<|endoftext|>


<commit_before>// Copyright 2005 The Closure Library Authors. 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.

/**
 * @fileoverview A class that can be used to listen to font size changes.
 */

goog.provide('goog.dom.FontSizeMonitor');
goog.provide('goog.dom.FontSizeMonitor.EventType');

goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.userAgent');

// TODO(user): Move this to goog.events instead.

/**
 * This class can be used to monitor changes in font size.  Instances will
 * dispatch a {@code goog.dom.FontSizeMonitor.EventType.CHANGE} event.
 * Example usage:
 *

 * var fms = new goog.dom.FontSizeMonitor();
 * goog.events.listen(fms, goog.dom.FontSizeMonitor.EventType.CHANGE,
 *     function(e) {
 *       alert('Font size was changed');
 *     });
 *

 * @param {goog.dom.DomHelper=} opt_domHelper DOM helper object that is used to
 *     determine where to insert the DOM nodes used to determine when the font
 *     size changes.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.dom.FontSizeMonitor = function(opt_domHelper) {
  goog.events.EventTarget.call(this);

  var dom = opt_domHelper || goog.dom.getDomHelper();

  /**
   * Offscreen iframe which we use to detect resize events.
   * @type {Element}
   * @private
   */
  this.sizeElement_ = dom.createDom(
      // The size of the iframe is expressed in em, which are font size relative
      // which will cause the iframe to be resized when the font size changes.
      // The actual values are not relevant as long as we can ensure that the
      // iframe has a non zero size and is completely off screen.
      goog.userAgent.IE ? 'div' : 'iframe',
      {'style': 'position:absolute;width:9em;height:9em;top:-99em'});
  var p = dom.getDocument().body;
  p.insertBefore(this.sizeElement_, p.firstChild);

  /**
   * The object that we listen to resize events on.
   * @type {Element|Window}
   * @private
   */
  var resizeTarget = this.resizeTarget_ =
      goog.userAgent.IE ? this.sizeElement_ :
          goog.dom.getFrameContentWindow(
              /** @type {HTMLIFrameElement} */ (this.sizeElement_));

  // We need to open and close the document to get Firefox 2 to work.  We must
  // not do this for IE in case we are using HTTPS since accessing the document
  // on an about:blank iframe in IE using HTTPS raises a Permission Denied
  // error. Additionally, firefox shows this frame in tab order by default,
  // suppress it by using a tabindex of -1.
  if (goog.userAgent.GECKO) {
    this.sizeElement_.tabIndex = -1;
    var doc = resizeTarget.document;
    doc.open();
    doc.close();
  }

  // Listen to resize event on the window inside the iframe.
  goog.events.listen(resizeTarget, goog.events.EventType.RESIZE,
                     this.handleResize_, false, this);

  /**
   * Last measured width of the iframe element.
   * @type {number}
   * @private
   */
  this.lastWidth_ = this.sizeElement_.offsetWidth;
};
goog.inherits(goog.dom.FontSizeMonitor, goog.events.EventTarget);

/**
 * The event types that the FontSizeMonitor fires.
 * @enum {string}
 */
goog.dom.FontSizeMonitor.EventType = {
  // TODO(user): Change value to 'change' after updating the callers.
  CHANGE: 'fontsizechange'
};

/**
 * Constant for the change event.
 * @type {string}
 * @deprecated Use {@code goog.dom.FontSizeMonitor.EventType.CHANGE} instead.
 */
goog.dom.FontSizeMonitor.CHANGE_EVENT =
    goog.dom.FontSizeMonitor.EventType.CHANGE;

/**
 * Disposes of the font size monitor.
 */
goog.dom.FontSizeMonitor.prototype.disposeInternal = function() {
  goog.dom.FontSizeMonitor.superClass_.disposeInternal.call(this);

  goog.events.unlisten(this.resizeTarget_, goog.events.EventType.RESIZE,
                       this.handleResize_, false, this);
  this.resizeTarget_ = null;

  // Firefox 2 crashes if the iframe is removed during the unload phase.
  if (!goog.userAgent.GECKO || goog.userAgent.isVersion('1.9')) {
    goog.dom.removeNode(this.sizeElement_);
  }
  delete this.sizeElement_;
};

/**
 * Handles the onresize event of the iframe and dispatches a change event in
 * case its size really changed.
 * @param {goog.events.BrowserEvent} e The event object.
 * @private
 */
goog.dom.FontSizeMonitor.prototype.handleResize_ = function(e) {
  // Only dispatch the event if the size really changed.  Some newer browsers do
  // not really change the font-size,  instead they zoom the whole page.  This
  // does trigger window resize events on the iframe but the logical pixel size
  // remains the same (the device pixel size changes but that is irrelevant).
  var currentWidth = this.sizeElement_.offsetWidth;
  if (this.lastWidth_ != currentWidth) {
    this.lastWidth_ = currentWidth;
    this.dispatchEvent(goog.dom.FontSizeMonitor.EventType.CHANGE);
  }
};
<commit_msg><commit_after>// Copyright 2005 The Closure Library Authors. 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.

/**
 * @fileoverview A class that can be used to listen to font size changes.
 */

goog.provide('goog.dom.FontSizeMonitor');
goog.provide('goog.dom.FontSizeMonitor.EventType');

goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.userAgent');

// TODO(user): Move this to goog.events instead.

/**
 * This class can be used to monitor changes in font size.  Instances will
 * dispatch a {@code goog.dom.FontSizeMonitor.EventType.CHANGE} event.
 * Example usage:
 *

 * var fms = new goog.dom.FontSizeMonitor();
 * goog.events.listen(fms, goog.dom.FontSizeMonitor.EventType.CHANGE,
 *     function(e) {
 *       alert('Font size was changed');
 *     });
 *

 * @param {goog.dom.DomHelper=} opt_domHelper DOM helper object that is used to
 *     determine where to insert the DOM nodes used to determine when the font
 *     size changes.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.dom.FontSizeMonitor = function(opt_domHelper) {
  goog.events.EventTarget.call(this);

  var dom = opt_domHelper || goog.dom.getDomHelper();

  /**
   * Offscreen iframe which we use to detect resize events.
   * @type {Element}
   * @private
   */
  this.sizeElement_ = dom.createDom(
      // The size of the iframe is expressed in em, which are font size relative
      // which will cause the iframe to be resized when the font size changes.
      // The actual values are not relevant as long as we can ensure that the
      // iframe has a non zero size and is completely off screen.
      goog.userAgent.IE ? 'div' : 'iframe',
      {'style': 'position:absolute;width:9em;height:9em;top:-99em;z-index:-9'});
  var p = dom.getDocument().body;
  p.insertBefore(this.sizeElement_, p.firstChild);

  /**
   * The object that we listen to resize events on.
   * @type {Element|Window}
   * @private
   */
  var resizeTarget = this.resizeTarget_ =
      goog.userAgent.IE ? this.sizeElement_ :
          goog.dom.getFrameContentWindow(
              /** @type {HTMLIFrameElement} */ (this.sizeElement_));

  // We need to open and close the document to get Firefox 2 to work.  We must
  // not do this for IE in case we are using HTTPS since accessing the document
  // on an about:blank iframe in IE using HTTPS raises a Permission Denied
  // error. Additionally, firefox shows this frame in tab order by default,
  // suppress it by using a tabindex of -1.
  if (goog.userAgent.GECKO) {
    this.sizeElement_.tabIndex = -1;
    var doc = resizeTarget.document;
    doc.open();
    doc.close();
  }

  // Listen to resize event on the window inside the iframe.
  goog.events.listen(resizeTarget, goog.events.EventType.RESIZE,
                     this.handleResize_, false, this);

  /**
   * Last measured width of the iframe element.
   * @type {number}
   * @private
   */
  this.lastWidth_ = this.sizeElement_.offsetWidth;
};
goog.inherits(goog.dom.FontSizeMonitor, goog.events.EventTarget);

/**
 * The event types that the FontSizeMonitor fires.
 * @enum {string}
 */
goog.dom.FontSizeMonitor.EventType = {
  // TODO(user): Change value to 'change' after updating the callers.
  CHANGE: 'fontsizechange'
};

/**
 * Constant for the change event.
 * @type {string}
 * @deprecated Use {@code goog.dom.FontSizeMonitor.EventType.CHANGE} instead.
 */
goog.dom.FontSizeMonitor.CHANGE_EVENT =
    goog.dom.FontSizeMonitor.EventType.CHANGE;

/**
 * Disposes of the font size monitor.
 */
goog.dom.FontSizeMonitor.prototype.disposeInternal = function() {
  goog.dom.FontSizeMonitor.superClass_.disposeInternal.call(this);

  goog.events.unlisten(this.resizeTarget_, goog.events.EventType.RESIZE,
                       this.handleResize_, false, this);
  this.resizeTarget_ = null;

  // Firefox 2 crashes if the iframe is removed during the unload phase.
  if (!goog.userAgent.GECKO || goog.userAgent.isVersion('1.9')) {
    goog.dom.removeNode(this.sizeElement_);
  }
  delete this.sizeElement_;
};

/**
 * Handles the onresize event of the iframe and dispatches a change event in
 * case its size really changed.
 * @param {goog.events.BrowserEvent} e The event object.
 * @private
 */
goog.dom.FontSizeMonitor.prototype.handleResize_ = function(e) {
  // Only dispatch the event if the size really changed.  Some newer browsers do
  // not really change the font-size,  instead they zoom the whole page.  This
  // does trigger window resize events on the iframe but the logical pixel size
  // remains the same (the device pixel size changes but that is irrelevant).
  var currentWidth = this.sizeElement_.offsetWidth;
  if (this.lastWidth_ != currentWidth) {
    this.lastWidth_ = currentWidth;
    this.dispatchEvent(goog.dom.FontSizeMonitor.EventType.CHANGE);
  }
};
<|endoftext|>


<commit_before>// Copyright 2006 The Closure Library Authors. 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.

/**
 * @fileoverview Utility for fast string concatenation.
 */

goog.provide('goog.string.StringBuffer');

/**
 * Utility class to facilitate string concatenation.
 *
 * @param {Object|number|string|boolean=} opt_a1 Optional first initial item
 *     to append.
 * @param {...Object|number|string|boolean} var_args Other initial items to
 *     append, e.g., new goog.string.StringBuffer('foo', 'bar').
 * @constructor
 */
goog.string.StringBuffer = function(opt_a1, var_args) {
  if (opt_a1 != null) {
    this.append.apply(this, arguments);
  }
};

/**
 * Internal buffer for the string to be concatenated.
 * @type {string}
 * @private
 */
goog.string.StringBuffer.prototype.buffer_ = '';

/**
 * Sets the contents of the string buffer object, replacing what's currently
 * there.
 *
 * @param {Object|number|string|boolean} s String to set.
 */
goog.string.StringBuffer.prototype.set = function(s) {
  this.buffer_ = '' + s;
};

/**
 * Appends one or more items to the buffer.
 *
 * Calling this with null, undefined, or empty arguments is an error.
 *
 * @param {*} a1 Required first string.
 * @param {*=} opt_a2 Optional second string.
 * @param {...*} var_args Other items to append,
 *     e.g., sb.append('foo', 'bar', 'baz').
 * @return {goog.string.StringBuffer} This same StringBuffer object.
 * @suppress {duplicate}
 */
goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
  // Use a1 directly to avoid arguments instantiation for single-arg case.
  this.buffer_ += a1;
  if (opt_a2 != null) { // second argument is undefined (null == undefined)
    for (var i = 1; i < arguments.length; i++) {
      this.buffer_ += arguments[i];
    }
  }
  return this;
};

/**
 * Clears the internal buffer.
 */
goog.string.StringBuffer.prototype.clear = function() {
  this.buffer_ = '';
};

/**
 * @return {number} the length of the current contents of the buffer.
 */
goog.string.StringBuffer.prototype.getLength = function() {
  return this.buffer_.length;
};

/**
 * @return {string} The concatenated string.
 * @override
 */
goog.string.StringBuffer.prototype.toString = function() {
  return this.buffer_;
};
<commit_msg><commit_after>// Copyright 2006 The Closure Library Authors. 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.

/**
 * @fileoverview Utility for fast string concatenation.
 */

goog.provide('goog.string.StringBuffer');

/**
 * Utility class to facilitate string concatenation.
 *
 * @param {Object|number|string|boolean=} opt_a1 Optional first initial item
 *     to append.
 * @param {...Object|number|string|boolean} var_args Other initial items to
 *     append, e.g., new goog.string.StringBuffer('foo', 'bar').
 * @constructor
 */
goog.string.StringBuffer = function(opt_a1, var_args) {
  if (opt_a1 != null) {
    this.append.apply(this, arguments);
  }
};

/**
 * Internal buffer for the string to be concatenated.
 * @type {string}
 * @private
 */
goog.string.StringBuffer.prototype.buffer_ = '';

/**
 * Sets the contents of the string buffer object, replacing what's currently
 * there.
 *
 * @param {Object|number|string|boolean} s String to set.
 */
goog.string.StringBuffer.prototype.set = function(s) {
  this.buffer_ = '' + s;
};

/**
 * Appends one or more items to the buffer.
 *
 * Calling this with null, undefined, or empty arguments is an error.
 *
 * @param {Object|number|string|boolean} a1 Required first string.
 * @param {Object|number|string|boolean=} opt_a2 Optional second string.
 * @param {...Object|number|string|boolean} var_args Other items to append,
 *     e.g., sb.append('foo', 'bar', 'baz').
 * @return {goog.string.StringBuffer} This same StringBuffer object.
 * @suppress {duplicate}
 */
goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
  // Use a1 directly to avoid arguments instantiation for single-arg case.
  this.buffer_ += a1;
  if (opt_a2 != null) { // second argument is undefined (null == undefined)
    for (var i = 1; i < arguments.length; i++) {
      this.buffer_ += arguments[i];
    }
  }
  return this;
};

/**
 * Clears the internal buffer.
 */
goog.string.StringBuffer.prototype.clear = function() {
  this.buffer_ = '';
};

/**
 * @return {number} the length of the current contents of the buffer.
 */
goog.string.StringBuffer.prototype.getLength = function() {
  return this.buffer_.length;
};

/**
 * @return {string} The concatenated string.
 * @override
 */
goog.string.StringBuffer.prototype.toString = function() {
  return this.buffer_;
};

<|endoftext|>


<commit_before>/* eslint max-len:0 */

export default {
  title: `JavaScript Utils & Lodash`,
  guests: [
    {
      name: 'John Dalton',
      twitter: 'jdalton',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
  ],
  description: `
    Lodash v4 had been released (by the time this episode happens). This JavaScript library of utilities is one of the most widely used utility belts of the enormous JavaScript ecosystem. We're going to talk with John, the mastermind behind this utility (and fork of underscore) about his experience building such a ubiquitous tool and what it can do for you.
  `,
  hangoutId: 'c143rgrflt08nui9n5h42morjbs',
  youTubeId: 'pqZGVV35BPU',
  podbeanId: '',
  host: {
    links: [
    ],
    tips: [
    ],
    picks: [
    ],
  },
  panelists: [
    {
      twitter: 'dan_abramov',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'drboolean',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'getify',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'kwuchu',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'linclark',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'mzabriskie',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'pamasaur',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'tylermcginnis33',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
  ],
}

<commit_msg>Add short url<commit_after>/* eslint max-len:0 */

export default {
  title: `JavaScript Utils & Lodash`,
  guests: [
    {
      name: 'John Dalton',
      twitter: 'jdalton',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
  ],
  description: `
    Lodash v4 had been released (by the time this episode happens). This JavaScript library of utilities is one of the most widely used utility belts of the enormous JavaScript ecosystem. We're going to talk with John, the mastermind behind this utility (and fork of underscore) about his experience building such a ubiquitous tool and what it can do for you.
  `,
  hangoutId: 'c143rgrflt08nui9n5h42morjbs',
  youTubeId: 'pqZGVV35BPU',
  podbeanId: '',
  shortUrl: 'http://jsair.io/lodash',
  host: {
    links: [
    ],
    tips: [
    ],
    picks: [
    ],
  },
  panelists: [
    {
      twitter: 'dan_abramov',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'drboolean',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'getify',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'kwuchu',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'linclark',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'mzabriskie',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'pamasaur',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
    {
      twitter: 'tylermcginnis33',
      links: [
      ],
      tips: [
      ],
      picks: [
      ],
    },
  ],
}

<|endoftext|>


<commit_before>
/*=============================================================
    Authour URI: www.binarycart.com
    Version: 1.1
    License: MIT

    http://opensource.org/licenses/MIT

    100% To use For Personal And Commercial Use.

    ========================================================  */

(function ($) {
    "use strict";
    var mainApp = {

        main_fun: function () {
            /*====================================
            METIS MENU
            ======================================*/
            $('#main-menu').metisMenu();

            /*====================================
              LOAD APPROPRIATE MENU BAR
           ======================================*/
            $(window).bind("load resize", function () {
                if ($(this).width() < 768) {
                    $('div.sidebar-collapse').addClass('collapse')
                } else {
                    $('div.sidebar-collapse').removeClass('collapse')
                }
            });

            /*====================================
            MORRIS BAR CHART
         ======================================*/
            Morris.Bar({
                element: 'morris-bar-chart',
                data: [{
                    y: '2006',
                    a: 100,
                    b: 90
                }, {
                    y: '2007',
                    a: 75,
                    b: 65
                }, {
                    y: '2008',
                    a: 50,
                    b: 40
                }, {
                    y: '2009',
                    a: 75,
                    b: 65
                }, {
                    y: '2010',
                    a: 50,
                    b: 40
                }, {
                    y: '2011',
                    a: 75,
                    b: 65
                }, {
                    y: '2012',
                    a: 100,
                    b: 90
                }],
                xkey: 'y',
                ykeys: ['a', 'b'],
                labels: ['Series A', 'Series B'],
                hideHover: 'auto',
                resize: true
            });

            /*====================================
          MORRIS DONUT CHART
       ======================================*/
            Morris.Donut({
                element: 'morris-donut-chart',
                data: [{
                    label: "Download Sales",
                    value: 12
                }, {
                    label: "In-Store Sales",
                    value: 30
                }, {
                    label: "Mail-Order Sales",
                    value: 20
                }],
                resize: true
            });

            /*====================================
         MORRIS AREA CHART
      ======================================*/

            Morris.Area({
                element: 'morris-area-chart',
                data: [{
                    period: '2010 Q1',
                    iphone: 2666,
                    ipad: null,
                    itouch: 2647
                }, {
                    period: '2010 Q2',
                    iphone: 2778,
                    ipad: 2294,
                    itouch: 2441
                }, {
                    period: '2010 Q3',
                    iphone: 4912,
                    ipad: 1969,
                    itouch: 2501
                }, {
                    period: '2010 Q4',
                    iphone: 3767,
                    ipad: 3597,
                    itouch: 5689
                }, {
                    period: '2011 Q1',
                    iphone: 6810,
                    ipad: 1914,
                    itouch: 2293
                }, {
                    period: '2011 Q2',
                    iphone: 5670,
                    ipad: 4293,
                    itouch: 1881
                }, {
                    period: '2011 Q3',
                    iphone: 4820,
                    ipad: 3795,
                    itouch: 1588
                }, {
                    period: '2011 Q4',
                    iphone: 15073,
                    ipad: 5967,
                    itouch: 5175
                }, {
                    period: '2012 Q1',
                    iphone: 10687,
                    ipad: 4460,
                    itouch: 2028
                }, {
                    period: '2012 Q2',
                    iphone: 8432,
                    ipad: 5713,
                    itouch: 1791
                }],
                xkey: 'period',
                ykeys: ['iphone', 'ipad', 'itouch'],
                labels: ['iPhone', 'iPad', 'iPod Touch'],
                pointSize: 2,
                hideHover: 'auto',
                resize: true
            });

            /*====================================
    MORRIS LINE CHART
 ======================================*/
            Morris.Line({
                element: 'morris-line-chart',
                data: [{
                    y: '2006',
                    a: 100,
                    b: 90
                }, {
                    y: '2007',
                    a: 75,
                    b: 65
                }, {
                    y: '2008',
                    a: 50,
                    b: 40
                }, {
                    y: '2009',
                    a: 75,
                    b: 65
                }, {
                    y: '2010',
                    a: 50,
                    b: 40
                }, {
                    y: '2011',
                    a: 75,
                    b: 65
                }, {
                    y: '2012',
                    a: 100,
                    b: 90
                }],
                xkey: 'y',
                ykeys: ['a', 'b'],
                labels: ['Series A', 'Series B'],
                hideHover: 'auto',
                resize: true
            });

        },

        initialization: function () {
            mainApp.main_fun();

        }

    }
    // Initializing ///

    $(document).ready(function () {
        mainApp.main_fun();
    });

}(jQuery));
<commit_msg>Implementação da função que valida a senha do cadastro.<commit_after>
function validaSenha (input){
    if (input.value != document.getElementById("senhaCadastro").value) {
    input.setCustomValidity('Repita a senha corretamente');
  } else {
    input.setCustomValidity('');
  }
}

(function ($) {
    "use strict";
    var mainApp = {

        main_fun: function () {
            /*====================================
            METIS MENU
            ======================================*/
            $('#main-menu').metisMenu();

            /*====================================
              LOAD APPROPRIATE MENU BAR
           ======================================*/
            $(window).bind("load resize", function () {
                if ($(this).width() < 768) {
                    $('div.sidebar-collapse').addClass('collapse')
                } else {
                    $('div.sidebar-collapse').removeClass('collapse')
                }
            });

            /*====================================
            MORRIS BAR CHART
         ======================================*/
            Morris.Bar({
                element: 'morris-bar-chart',
                data: [{
                    y: '2006',
                    a: 100,
                    b: 90
                }, {
                    y: '2007',
                    a: 75,
                    b: 65
                }, {
                    y: '2008',
                    a: 50,
                    b: 40
                }, {
                    y: '2009',
                    a: 75,
                    b: 65
                }, {
                    y: '2010',
                    a: 50,
                    b: 40
                }, {
                    y: '2011',
                    a: 75,
                    b: 65
                }, {
                    y: '2012',
                    a: 100,
                    b: 90
                }],
                xkey: 'y',
                ykeys: ['a', 'b'],
                labels: ['Series A', 'Series B'],
                hideHover: 'auto',
                resize: true
            });

            /*====================================
          MORRIS DONUT CHART
       ======================================*/
            Morris.Donut({
                element: 'morris-donut-chart',
                data: [{
                    label: "Download Sales",
                    value: 12
                }, {
                    label: "In-Store Sales",
                    value: 30
                }, {
                    label: "Mail-Order Sales",
                    value: 20
                }],
                resize: true
            });

            /*====================================
         MORRIS AREA CHART
      ======================================*/

            Morris.Area({
                element: 'morris-area-chart',
                data: [{
                    period: '2010 Q1',
                    iphone: 2666,
                    ipad: null,
                    itouch: 2647
                }, {
                    period: '2010 Q2',
                    iphone: 2778,
                    ipad: 2294,
                    itouch: 2441
                }, {
                    period: '2010 Q3',
                    iphone: 4912,
                    ipad: 1969,
                    itouch: 2501
                }, {
                    period: '2010 Q4',
                    iphone: 3767,
                    ipad: 3597,
                    itouch: 5689
                }, {
                    period: '2011 Q1',
                    iphone: 6810,
                    ipad: 1914,
                    itouch: 2293
                }, {
                    period: '2011 Q2',
                    iphone: 5670,
                    ipad: 4293,
                    itouch: 1881
                }, {
                    period: '2011 Q3',
                    iphone: 4820,
                    ipad: 3795,
                    itouch: 1588
                }, {
                    period: '2011 Q4',
                    iphone: 15073,
                    ipad: 5967,
                    itouch: 5175
                }, {
                    period: '2012 Q1',
                    iphone: 10687,
                    ipad: 4460,
                    itouch: 2028
                }, {
                    period: '2012 Q2',
                    iphone: 8432,
                    ipad: 5713,
                    itouch: 1791
                }],
                xkey: 'period',
                ykeys: ['iphone', 'ipad', 'itouch'],
                labels: ['iPhone', 'iPad', 'iPod Touch'],
                pointSize: 2,
                hideHover: 'auto',
                resize: true
            });

            /*====================================
    MORRIS LINE CHART
 ======================================*/
            Morris.Line({
                element: 'morris-line-chart',
                data: [{
                    y: '2006',
                    a: 100,
                    b: 90
                }, {
                    y: '2007',
                    a: 75,
                    b: 65
                }, {
                    y: '2008',
                    a: 50,
                    b: 40
                }, {
                    y: '2009',
                    a: 75,
                    b: 65
                }, {
                    y: '2010',
                    a: 50,
                    b: 40
                }, {
                    y: '2011',
                    a: 75,
                    b: 65
                }, {
                    y: '2012',
                    a: 100,
                    b: 90
                }],
                xkey: 'y',
                ykeys: ['a', 'b'],
                labels: ['Series A', 'Series B'],
                hideHover: 'auto',
                resize: true
            });

        },

        initialization: function () {
            mainApp.main_fun();

        }

    }
    // Initializing ///

    $(document).ready(function () {
        mainApp.main_fun();
    });

}(jQuery));
<|endoftext|>


<commit_before>'use strict';

var imports        = require('soop').imports();
var util           = require('util');
var async          = require('async');

var bitcore        = require('bitcore');
var networks       = bitcore.networks;
var config         = imports.config || require('../config/config');
var Sync           = require('./Sync');
var sockets        = require('../app/controllers/socket.js');
var BlockExtractor = require('./BlockExtractor.js');
var buffertools    = require('buffertools');
var bitcoreUtil    = bitcore.util;
var logger = require('./logger').logger;
var info   = logger.info;
var error  = logger.error;

//  var Deserialize = require('bitcore/Deserialize');
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';

var BAD_GEN_ERROR_DB = 'Bad genesis block. Network mismatch between Insight and levelDB? Insight is configured for:';
function HistoricSync(opts) {
  opts = opts || {};
  this.shouldBroadcast = opts.shouldBroadcastSync;

  this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;

  var genesisHashReversed = new Buffer(32);
  this.network.genesisBlock.hash.copy(genesisHashReversed);
  buffertools.reverse(genesisHashReversed);
  this.genesis = genesisHashReversed.toString('hex');

	var bitcore        = require('bitcore');
	var RpcClient      = bitcore.RpcClient;

  this.rpc = new RpcClient(config.bitcoind);
  this.sync = new Sync(opts);
  this.height =0;
}

HistoricSync.prototype.showProgress = function() {
  var self = this;

  if ( self.status ==='syncing' &&
      ( self.height )  % self.step !== 1)  return;

  if (self.error)
    error(self.error);

  else {
    self.updatePercentage();
    info(util.format('status: [%d%%]', self.syncPercentage));
  }
  if (self.shouldBroadcast) {
    sockets.broadcastSyncInfo(self.info());
  }
  //
  // if (self.syncPercentage > 10) {
  //   process.exit(-1);
  // }
};

HistoricSync.prototype.setError = function(err) {
  var self = this;
  self.error = err.message?err.message:err.toString();
  self.status='error';
  self.showProgress();
  return err;
};

HistoricSync.prototype.close = function() {
  this.sync.close();
};

HistoricSync.prototype.info = function() {
  this.updatePercentage();
  return {
    status: this.status,
    blockChainHeight: this.blockChainHeight,
    syncPercentage: this.syncPercentage,
    height: this.height,
    syncTipHash: this.sync.tip,
    error: this.error,
    type: this.type,
    startTs: this.startTs,
    endTs: this.endTs,
  };
};

HistoricSync.prototype.updatePercentage = function() {
  var r = this.height  / this.blockChainHeight;
  this.syncPercentage = parseFloat(100 * r).toFixed(3);
  if (this.syncPercentage > 100) this.syncPercentage = 100;
};

HistoricSync.prototype.getBlockFromRPC = function(cb) {
  var self = this;

  if (!self.currentRpcHash) return cb();

  var blockInfo;
  self.rpc.getBlock(self.currentRpcHash, function(err, ret) {
    if (err) return cb(err);
    if (ret) {
      blockInfo = ret.result;
      // this is to match block retreived from file
      if (blockInfo.hash === self.genesis)
        blockInfo.previousblockhash =
          self.network.genesisBlock.prev_hash.toString('hex');

      self.currentRpcHash = blockInfo.nextblockhash;
    }
    else {
      blockInfo = null;
    }
    return cb(null, blockInfo);
  });
};

HistoricSync.prototype.getStandardizedBlock = function(b) {
  var self = this;

  var block = {
    hash: bitcoreUtil.formatHashFull(b.getHash()),
    previousblockhash: bitcoreUtil.formatHashFull(b.prev_hash),
    time: b.timestamp,
  };
  var isCoinBase = 1;
  block.tx = b.txs.map(function(tx){
    var ret = self.sync.txDb.getStandardizedTx(tx, b.timestamp, isCoinBase);
    isCoinBase=0;
    return ret;
  });
  return block;
};

HistoricSync.prototype.getBlockFromFile = function(cb) {
  var self = this;

  var blockInfo;

  //get Info
  self.blockExtractor.getNextBlock(function(err, b) {
    if (err || ! b) return cb(err);
    blockInfo = self.getStandardizedBlock(b);
    self.sync.bDb.setLastFileIndex(self.blockExtractor.currentFileIndex, function(err) {
      return cb(err,blockInfo);
    });
  });
};

HistoricSync.prototype.updateBlockChainHeight = function(cb) {
  var self = this;

  self.rpc.getBlockCount(function(err, res) {
    self.blockChainHeight = res.result;
    return cb(err);
  });
};

HistoricSync.prototype.checkNetworkSettings = function(next) {
  var self = this;

  self.hasGenesis = false;

  // check network config
  self.rpc.getBlockHash(0, function(err, res){
    if (!err && ( res && res.result !== self.genesis)) {
      err = new Error(BAD_GEN_ERROR + config.network);
    }
    if (err) return next(err);
    self.sync.bDb.has(self.genesis, function(err, b) {
      if (!err && ( res && res.result !== self.genesis)) {
        err = new Error(BAD_GEN_ERROR_DB + config.network);
      }
      self.hasGenesis = b?true:false;
      return next(err);
    });
  });
};

HistoricSync.prototype.updateStartBlock = function(next) {
  var self = this;

  self.startBlock = self.genesis;

  self.sync.bDb.getTip(function(err,tip, height) {
    if (!tip) return next();

    var blockInfo;
    var oldtip;

    //check that the tip is still on the mainchain
    async.doWhilst(
      function(cb) {
        self.sync.bDb.fromHashWithInfo(tip, function(err, bi) {
          blockInfo = bi ? bi.info : {};
          if (oldtip)
            self.sync.setBlockHeight(oldtip, -1, cb);
          else
            return cb();
        });
      },
      function(err) {
        if (err) return next(err);
        var ret = false;

        var d = Math.abs(height-blockInfo.height);
        if (d>6) {
          error('Previous Tip block tip height differs by %d. Please delete and resync (-D)',d);
          process.exit(1);
        }
        if ( self.blockChainHeight  === blockInfo.height ||
            blockInfo.confirmations > 0) {
          ret = false;
        }
        else {
          oldtip = tip;
          if (!tip)
            throw new Error('Previous blockchain tip was not found on bitcoind. Please reset Insight DB. Tip was:'+tip)
          tip = blockInfo.previousblockhash;
          info('Previous TIP is now orphan. Back to:' + tip);
          ret  = true;
        }
        return ret;
      },
      function(err) {
        self.startBlock = tip;
        self.height = height;
        info('Resuming sync from block: %s #%d',tip,height);
        return next(err);
      }
    );
  });
};

HistoricSync.prototype.prepareFileSync = function(opts, next) {
  var self = this;

  if ( opts.forceRPC || !config.bitcoind.dataDir ||
    self.height > self.blockChainHeight * 0.9) return next();

  try {
  self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network);
  } catch (e) {
    info(e.message + '. Disabling file sync.');
    return next();
  }

  self.getFn = self.getBlockFromFile;
  self.allowReorgs = true;
  self.sync.bDb.getLastFileIndex(function(err, idx) {

    if (opts.forceStartFile)
      self.blockExtractor.currentFileIndex = opts.forceStartFile;
    else if (idx) self.blockExtractor.currentFileIndex = idx;

    var h = self.genesis;

    info('Seeking file to:' + self.startBlock);
    //forward till startBlock
    async.whilst(
      function() {
        return h !== self.startBlock;
      },
      function (w_cb) {
        self.getBlockFromFile(function(err,b) {
          if (!b) return w_cb('Could not find block ' + self.startBlock);
          h=b.hash;
          setImmediate(function(){
            return w_cb(err);
          });
        });
      }, function(err){
        return next(err);
      });
  });
};

//NOP
HistoricSync.prototype.prepareRpcSync = function(opts, next) {
  var self = this;

  if (self.blockExtractor) return next();
  self.getFn = self.getBlockFromRPC;
  self.allowReorgs = true;
  self.currentRpcHash  = self.startBlock;
  return next();
};

HistoricSync.prototype.showSyncStartMessage = function() {
  var self = this;

  info('Got ' + self.height +
    ' blocks in current DB, out of ' + self.blockChainHeight + ' block at bitcoind');

  if (self.blockExtractor) {
    info('bitcoind dataDir configured...importing blocks from .dat files');
    info('First file index: ' + self.blockExtractor.currentFileIndex);
  }
  else {
    info('syncing from RPC (slow)');
  }

  info('Starting from: ', self.startBlock);
  self.showProgress();
};

HistoricSync.prototype.setupSyncStatus = function() {
  var self = this;

  var step = parseInt( (self.blockChainHeight - self.height) / 1000);
  if (step < 10) step = 10;

  self.step = step;
  self.type   = self.blockExtractor?'from .dat Files':'from RPC calls';
  self.status = 'syncing';
  self.startTs = Date.now();
  self.endTs   = null;
  this.error  = null;
  this.syncPercentage = 0;
};

HistoricSync.prototype.checkDBVersion = function(cb) {
  this.sync.txDb.checkVersion02(function(isOk){
    if (!isOk) {
      console.log('\n#############################\n\n ## Insight API DB is older that v0.2. Please resync using:\n $ util/sync.js -D\n More information at Insight API\'s Readme.md');
      process.exit(1);
    }
    // Add more test here in future changes.
    return cb();
  });
};

HistoricSync.prototype.prepareToSync = function(opts, next) {
  var self = this;

  self.status = 'starting';
  async.series([
    function(s_c) {
      self.checkDBVersion(s_c);
    },
    function(s_c) {
      self.checkNetworkSettings(s_c);
    },
    function(s_c) {
      self.updateBlockChainHeight(s_c);
    },
    function(s_c) {
      self.updateStartBlock(s_c);
    },
    function(s_c) {
      self.prepareFileSync(opts, s_c);
    },
    function(s_c) {
      self.prepareRpcSync(opts, s_c);
    },
  ],
  function(err) {
    if (err)  return(self.setError(err));

    self.showSyncStartMessage();
    self.setupSyncStatus();
    return next();
  });
};

HistoricSync.prototype.start = function(opts, next) {
  var self = this;

  if (self.status==='starting' || self.status==='syncing') {
    error('## Wont start to sync while status is %s', self.status);
    return next();
  }

  self.prepareToSync(opts, function(err) {
    if (err) return next(self.setError(err));

    async.whilst(
      function() {
        self.showProgress();
        return self.status === 'syncing';
      },
      function (w_cb) {
        self.getFn(function(err,blockInfo) {
          if (err) return w_cb(self.setError(err));
          if (blockInfo && blockInfo.hash && (!opts.stopAt  || opts.stopAt !== blockInfo.hash)) {
            self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err, height) {
              if (err) return w_cb(self.setError(err));
              if (height>=0) self.height=height;
              setImmediate(function(){
                return w_cb(err);
              });
            });
          }
          else {
            self.endTs = Date.now();
            self.status = 'finished';
            console.log('Done Syncing', self.info());
            return w_cb(err);
          }
        });
      }, next);
  });
};

module.exports = require('soop')(HistoricSync);
<commit_msg>Fix crash during reorg: setBlockHeight replaced with setBlockNotMain in f0f936d2d8756902055b5b226085389b7c047478<commit_after>'use strict';

var imports        = require('soop').imports();
var util           = require('util');
var async          = require('async');

var bitcore        = require('bitcore');
var networks       = bitcore.networks;
var config         = imports.config || require('../config/config');
var Sync           = require('./Sync');
var sockets        = require('../app/controllers/socket.js');
var BlockExtractor = require('./BlockExtractor.js');
var buffertools    = require('buffertools');
var bitcoreUtil    = bitcore.util;
var logger = require('./logger').logger;
var info   = logger.info;
var error  = logger.error;

//  var Deserialize = require('bitcore/Deserialize');
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:';

var BAD_GEN_ERROR_DB = 'Bad genesis block. Network mismatch between Insight and levelDB? Insight is configured for:';
function HistoricSync(opts) {
  opts = opts || {};
  this.shouldBroadcast = opts.shouldBroadcastSync;

  this.network = config.network === 'testnet' ? networks.testnet: networks.livenet;

  var genesisHashReversed = new Buffer(32);
  this.network.genesisBlock.hash.copy(genesisHashReversed);
  buffertools.reverse(genesisHashReversed);
  this.genesis = genesisHashReversed.toString('hex');

	var bitcore        = require('bitcore');
	var RpcClient      = bitcore.RpcClient;

  this.rpc = new RpcClient(config.bitcoind);
  this.sync = new Sync(opts);
  this.height =0;
}

HistoricSync.prototype.showProgress = function() {
  var self = this;

  if ( self.status ==='syncing' &&
      ( self.height )  % self.step !== 1)  return;

  if (self.error)
    error(self.error);

  else {
    self.updatePercentage();
    info(util.format('status: [%d%%]', self.syncPercentage));
  }
  if (self.shouldBroadcast) {
    sockets.broadcastSyncInfo(self.info());
  }
  //
  // if (self.syncPercentage > 10) {
  //   process.exit(-1);
  // }
};

HistoricSync.prototype.setError = function(err) {
  var self = this;
  self.error = err.message?err.message:err.toString();
  self.status='error';
  self.showProgress();
  return err;
};

HistoricSync.prototype.close = function() {
  this.sync.close();
};

HistoricSync.prototype.info = function() {
  this.updatePercentage();
  return {
    status: this.status,
    blockChainHeight: this.blockChainHeight,
    syncPercentage: this.syncPercentage,
    height: this.height,
    syncTipHash: this.sync.tip,
    error: this.error,
    type: this.type,
    startTs: this.startTs,
    endTs: this.endTs,
  };
};

HistoricSync.prototype.updatePercentage = function() {
  var r = this.height  / this.blockChainHeight;
  this.syncPercentage = parseFloat(100 * r).toFixed(3);
  if (this.syncPercentage > 100) this.syncPercentage = 100;
};

HistoricSync.prototype.getBlockFromRPC = function(cb) {
  var self = this;

  if (!self.currentRpcHash) return cb();

  var blockInfo;
  self.rpc.getBlock(self.currentRpcHash, function(err, ret) {
    if (err) return cb(err);
    if (ret) {
      blockInfo = ret.result;
      // this is to match block retreived from file
      if (blockInfo.hash === self.genesis)
        blockInfo.previousblockhash =
          self.network.genesisBlock.prev_hash.toString('hex');

      self.currentRpcHash = blockInfo.nextblockhash;
    }
    else {
      blockInfo = null;
    }
    return cb(null, blockInfo);
  });
};

HistoricSync.prototype.getStandardizedBlock = function(b) {
  var self = this;

  var block = {
    hash: bitcoreUtil.formatHashFull(b.getHash()),
    previousblockhash: bitcoreUtil.formatHashFull(b.prev_hash),
    time: b.timestamp,
  };
  var isCoinBase = 1;
  block.tx = b.txs.map(function(tx){
    var ret = self.sync.txDb.getStandardizedTx(tx, b.timestamp, isCoinBase);
    isCoinBase=0;
    return ret;
  });
  return block;
};

HistoricSync.prototype.getBlockFromFile = function(cb) {
  var self = this;

  var blockInfo;

  //get Info
  self.blockExtractor.getNextBlock(function(err, b) {
    if (err || ! b) return cb(err);
    blockInfo = self.getStandardizedBlock(b);
    self.sync.bDb.setLastFileIndex(self.blockExtractor.currentFileIndex, function(err) {
      return cb(err,blockInfo);
    });
  });
};

HistoricSync.prototype.updateBlockChainHeight = function(cb) {
  var self = this;

  self.rpc.getBlockCount(function(err, res) {
    self.blockChainHeight = res.result;
    return cb(err);
  });
};

HistoricSync.prototype.checkNetworkSettings = function(next) {
  var self = this;

  self.hasGenesis = false;

  // check network config
  self.rpc.getBlockHash(0, function(err, res){
    if (!err && ( res && res.result !== self.genesis)) {
      err = new Error(BAD_GEN_ERROR + config.network);
    }
    if (err) return next(err);
    self.sync.bDb.has(self.genesis, function(err, b) {
      if (!err && ( res && res.result !== self.genesis)) {
        err = new Error(BAD_GEN_ERROR_DB + config.network);
      }
      self.hasGenesis = b?true:false;
      return next(err);
    });
  });
};

HistoricSync.prototype.updateStartBlock = function(next) {
  var self = this;

  self.startBlock = self.genesis;

  self.sync.bDb.getTip(function(err,tip, height) {
    if (!tip) return next();

    var blockInfo;
    var oldtip;

    //check that the tip is still on the mainchain
    async.doWhilst(
      function(cb) {
        self.sync.bDb.fromHashWithInfo(tip, function(err, bi) {
          blockInfo = bi ? bi.info : {};
          if (oldtip)
            self.sync.bDb.setBlockNotMain(oldtip, cb);
          else
            return cb();
        });
      },
      function(err) {
        if (err) return next(err);
        var ret = false;

        var d = Math.abs(height-blockInfo.height);
        if (d>6) {
          error('Previous Tip block tip height differs by %d. Please delete and resync (-D)',d);
          process.exit(1);
        }
        if ( self.blockChainHeight  === blockInfo.height ||
            blockInfo.confirmations > 0) {
          ret = false;
        }
        else {
          oldtip = tip;
          if (!tip)
            throw new Error('Previous blockchain tip was not found on bitcoind. Please reset Insight DB. Tip was:'+tip)
          tip = blockInfo.previousblockhash;
          info('Previous TIP is now orphan. Back to:' + tip);
          ret  = true;
        }
        return ret;
      },
      function(err) {
        self.startBlock = tip;
        self.height = height;
        info('Resuming sync from block: %s #%d',tip,height);
        return next(err);
      }
    );
  });
};

HistoricSync.prototype.prepareFileSync = function(opts, next) {
  var self = this;

  if ( opts.forceRPC || !config.bitcoind.dataDir ||
    self.height > self.blockChainHeight * 0.9) return next();

  try {
  self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network);
  } catch (e) {
    info(e.message + '. Disabling file sync.');
    return next();
  }

  self.getFn = self.getBlockFromFile;
  self.allowReorgs = true;
  self.sync.bDb.getLastFileIndex(function(err, idx) {

    if (opts.forceStartFile)
      self.blockExtractor.currentFileIndex = opts.forceStartFile;
    else if (idx) self.blockExtractor.currentFileIndex = idx;

    var h = self.genesis;

    info('Seeking file to:' + self.startBlock);
    //forward till startBlock
    async.whilst(
      function() {
        return h !== self.startBlock;
      },
      function (w_cb) {
        self.getBlockFromFile(function(err,b) {
          if (!b) return w_cb('Could not find block ' + self.startBlock);
          h=b.hash;
          setImmediate(function(){
            return w_cb(err);
          });
        });
      }, function(err){
        return next(err);
      });
  });
};

//NOP
HistoricSync.prototype.prepareRpcSync = function(opts, next) {
  var self = this;

  if (self.blockExtractor) return next();
  self.getFn = self.getBlockFromRPC;
  self.allowReorgs = true;
  self.currentRpcHash  = self.startBlock;
  return next();
};

HistoricSync.prototype.showSyncStartMessage = function() {
  var self = this;

  info('Got ' + self.height +
    ' blocks in current DB, out of ' + self.blockChainHeight + ' block at bitcoind');

  if (self.blockExtractor) {
    info('bitcoind dataDir configured...importing blocks from .dat files');
    info('First file index: ' + self.blockExtractor.currentFileIndex);
  }
  else {
    info('syncing from RPC (slow)');
  }

  info('Starting from: ', self.startBlock);
  self.showProgress();
};

HistoricSync.prototype.setupSyncStatus = function() {
  var self = this;

  var step = parseInt( (self.blockChainHeight - self.height) / 1000);
  if (step < 10) step = 10;

  self.step = step;
  self.type   = self.blockExtractor?'from .dat Files':'from RPC calls';
  self.status = 'syncing';
  self.startTs = Date.now();
  self.endTs   = null;
  this.error  = null;
  this.syncPercentage = 0;
};

HistoricSync.prototype.checkDBVersion = function(cb) {
  this.sync.txDb.checkVersion02(function(isOk){
    if (!isOk) {
      console.log('\n#############################\n\n ## Insight API DB is older that v0.2. Please resync using:\n $ util/sync.js -D\n More information at Insight API\'s Readme.md');
      process.exit(1);
    }
    // Add more test here in future changes.
    return cb();
  });
};

HistoricSync.prototype.prepareToSync = function(opts, next) {
  var self = this;

  self.status = 'starting';
  async.series([
    function(s_c) {
      self.checkDBVersion(s_c);
    },
    function(s_c) {
      self.checkNetworkSettings(s_c);
    },
    function(s_c) {
      self.updateBlockChainHeight(s_c);
    },
    function(s_c) {
      self.updateStartBlock(s_c);
    },
    function(s_c) {
      self.prepareFileSync(opts, s_c);
    },
    function(s_c) {
      self.prepareRpcSync(opts, s_c);
    },
  ],
  function(err) {
    if (err)  return(self.setError(err));

    self.showSyncStartMessage();
    self.setupSyncStatus();
    return next();
  });
};

HistoricSync.prototype.start = function(opts, next) {
  var self = this;

  if (self.status==='starting' || self.status==='syncing') {
    error('## Wont start to sync while status is %s', self.status);
    return next();
  }

  self.prepareToSync(opts, function(err) {
    if (err) return next(self.setError(err));

    async.whilst(
      function() {
        self.showProgress();
        return self.status === 'syncing';
      },
      function (w_cb) {
        self.getFn(function(err,blockInfo) {
          if (err) return w_cb(self.setError(err));
          if (blockInfo && blockInfo.hash && (!opts.stopAt  || opts.stopAt !== blockInfo.hash)) {
            self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err, height) {
              if (err) return w_cb(self.setError(err));
              if (height>=0) self.height=height;
              setImmediate(function(){
                return w_cb(err);
              });
            });
          }
          else {
            self.endTs = Date.now();
            self.status = 'finished';
            console.log('Done Syncing', self.info());
            return w_cb(err);
          }
        });
      }, next);
  });
};

module.exports = require('soop')(HistoricSync);
<|endoftext|>


<commit_before>/**
 * Created by tonigurski on 19.11.15.
 */

/**
 * preveneds Dropzone from autoloading
 * @type {boolean}
 */
Dropzone.autoDiscover = false;

/**
 * Helper to boot up the media bundle dropzone at the right moment
 */

(function (exports, d) {
    function MMMediaBundleDomReady(fn, context) {

        function onReady(event) {
            d.removeEventListener("DOMContentLoaded", onReady);
            fn.call(context || exports, event);
        }

        function onReadyIe(event) {
            if (d.readyState === "complete") {
                d.detachEvent("onreadystatechange", onReadyIe);
                fn.call(context || exports, event);
            }
        }

        d.addEventListener && d.addEventListener("DOMContentLoaded", onReady) ||
        d.attachEvent && d.attachEvent("onreadystatechange", onReadyIe);
    }

    exports.MMMediaBundleDomReady = MMMediaBundleDomReady;
})(window, document);

/**
 * Set ups a given dropzone to be compatible with the MMMediaBundleWidget
 *
 * @param _id
 * @param _url
 * @returns DropZone
 *
 * @constructor
 */
function MMMediaBundleFileDropzone(_id, _url, _fieldName, _multiple, _files, _options) {

    var $this = this;
    $this.id = _id;

    /**
     * creates and DOM input which holds a Media-Entity ID
     * @param _file
     * @returns {Element}
     */
    this.getHiddenEntityInput = function (_file) {

        if (!_file.id)
            return;

        var reference = document.createElement('input')
        reference.type = 'hidden';
        reference.name = _fieldName;
        reference.value = _file.id;

        return reference;
    };

    /**
     * appends an DOM input to the related previewElement of the given file-element
     * @param _file
     */
    this.appendHiddenEntityInput = function (_file) {

        if (_file) {

            var $reference = $this.getHiddenEntityInput(_file)

            if ($reference)
                _file.previewElement.appendChild($reference);
        }
    };

    /**
     * fires the afterInit event after the bundle finishs the custom init call
     */
    this.fireAfterInitEvent = function (_DropzoneDOM) {

        var $event;
        var $element = document.getElementById(this.id.replace('#', ''));

        if (document.createEvent) {
            $event = document.createEvent("HTMLEvents");

            $event.initEvent("MMMediaBundleFileDropzone.afterInit", true, true);
        } else {
            $event = document.createEventObject();
            $event.eventType = "MMMediaBundleFileDropzone.afterInit";
        }

        if (document.createEvent) {
            $element.dispatchEvent($event);
        } else {
            $element.fireEvent("on" + $event.eventType, $event);
        }
    };

    /**
     * function which get called by Dropzone after init
     */
    this.initCallback = function () {

        var myDropzone = this;

        /**
         * checks if its an mutiple field and adds the related event
         */
        if (_multiple) {

            /**
             * hooks the successmultiple event to append all hidden inputs which holds all tthe entity ids
             */
            myDropzone.on("successmultiple", function (files, response) {

                if (response && response.success) {

                    for (file in response.data) {

                        files[file].id = response.data[file].id;

                        $this.appendHiddenEntityInput(files[file]);

                    }

                }

            });
        }
        else {
            /**
             * hooks the sucess event to append a hidden input which holds the entity id
             */
            myDropzone.on("success", function (file, response) {

                if (response && response.success) {

                    for (tFile in response.data) {

                        file.id = response.data[0].id;

                        $this.appendHiddenEntityInput(file);
                    }
                }
            });
        }

        /**
         * add already stored files to the widget
         */

            //temporaty add this event to get the real formated dropzone-file objects
        myDropzone.on("addedfile", $this.appendHiddenEntityInput);

        //loops thought the presetted files and appends them to the dropzone_preview
        for (var i = 0; i < _files.length; i++) {
            var mock = _files[i];
            mock.accepted = true;

            myDropzone.files.push(mock);
            myDropzone.emit('addedfile', mock);

            myDropzone.createThumbnailFromUrl(mock, mock.url);
            myDropzone.emit('complete', mock);
        }

        //removes the temporaty added this event
        myDropzone.off("addedfile", $this.appendHiddenEntityInput);

        /**
         * loads Dragular to enable drag and drop functionality
         * @TODO: check if its possible to enable drag n drop with multiple instances of Dropzone
         */
        if (_multiple) {

            var $dragula = dragula([document.querySelector($id_dropzone_preview)],
                {
                    direction: 'horizontal',
                    mirrorContainer: document.querySelector($id_dropzone_preview)

                });
        }

        myDropzone.on("success", function (file, responseText) {
            myDropzone.responseText = responseText;
        });

        myDropzone.on("complete", function (file) {

            var tFile = file;
            var obj = null;

            /**
             * @Todo temporary fix for DropZone ...need to be removed if the file variable is fixed
             */
            setTimeout(function(){

                for (var i in myDropzone.responseText.data) {

                    if (myDropzone.responseText.data[i].id == tFile.id) {
                        obj = myDropzone.responseText.data[i];
                        break;
                    }
                }
                // Handle the responseText here. For example, add the text to the preview element:
                if (obj)
                    tFile.previewTemplate.querySelector('.dz-filename span').innerHTML = obj.path;
            },100);

        });

        $this.fireAfterInitEvent();

    };

    /**
     * dom id of preview container
     * @type {string}
     */
    var $id_dropzone_preview = _id + '_previews';

    var $options = {
        thumbnailWidth: 800,
        thumbnailHeight: 600,
        url: _url
        // The configuration we've talked about above
        , autoProcessQueue: true
        , paramName: (_multiple ? 'files' : 'files[]')
        , addRemoveLinks: true

        , previewsContainer: $id_dropzone_preview

        // multiple settings
        , uploadMultiple: _multiple
        , parallelUploads: ( (_multiple) ? 100 : 1 )
        , maxFiles: ( (_multiple) ? 100 : 1 )

        // The setting up of the dropzone
        , init: this.initCallback
    };

    /**
     * merge aditional given options
     */
    if (typeof _options == "object")
        for (var key in _options)
            $options[key] = _options[key];

    /**
     * loads the Dropzone instance
     */
    this.dropzone = new Dropzone(_id, $options);
}

function MMMediaBundleFileDropzoneInitiateEvents() {

}

var MMMediaBundleInit = function (event) {

    var elements = document.getElementsByClassName('mmmb-dropzone');

    for (var i = 0; i < elements.length; i++) {

        var dropzone = elements[i];

        var url = dropzone.getAttribute('data-url');
        var fieldName = dropzone.getAttribute('data-field-name');
        var id = dropzone.getAttribute('id');

        //fetches preloaded Files
        var files = dropzone.getAttribute('data-files');
        files = JSON.parse(files);

        // get the allowed filetypes
        var fileTypes = dropzone.getAttribute('data-accepted-files');
        fileTypes = fileTypes ? fileTypes : false;

        /*
         * @TODO: better bool check
         */
        var multiple = dropzone.getAttribute('data-multiple');

        if (multiple == "false" || multiple == "" || multiple == "0")
            multiple = false;
        else
            multiple = true;

        var _options = {

            acceptedFiles: fileTypes,

            dictDefaultMessage: dropzone.getAttribute('data-dictDefaultMessage'), // The message that gets displayed before any files are dropped. This is normally replaced by an image but defaults to "Drop files here to upload"'
            dictFallbackMessage: dropzone.getAttribute('data-dictFallbackMessage'), // If the browser is not supported, the default message will be replaced with this text. Defaults to "Your browser does not support drag'n'drop file uploads."'
            dictFallbackText: dropzone.getAttribute('data-dictFallbackText'), // This will be added before the file input files. If you provide a fallback element yourself, or if this option is null this will be ignored. Defaults to "Please use the fallback form below to upload your files like in the olden days."'
            dictInvalidFileType: dropzone.getAttribute('data-dictInvalidFileType'), // Shown as error message if the file doesn·'t match the file type.'
            dictFileTooBig: dropzone.getAttribute('data-dictFileTooBig'), // Shown when the file is too big. {{filesize}} and {{maxFilesize}} will be replaced.'
            dictResponseError: dropzone.getAttribute('data-dictResponseError'), // Shown as error message if the server response was invalid. {{statusCode}} will be replaced with the servers status code.'
            dictCancelUpload: dropzone.getAttribute('data-dictCancelUpload'), // If addRemoveLinks is true, the text to be used for the cancel upload link.'
            dictCancelUploadConfirmation: dropzone.getAttribute('data-dictCancelUploadConfirmation'), // If addRemoveLinks is true, the text to be used for confirmation when cancelling upload.'
            dictRemoveFile: dropzone.getAttribute('data-dictRemoveFile'), // If addRemoveLinks is true, the text to be used to remove a file.'
            dictRemoveFileConfirmation: dropzone.getAttribute('data-dictRemoveFileConfirmation'), //
            dictMaxFilesExceeded: dropzone.getAttribute('data-dictMaxFilesExceeded')  // If maxFiles is set, this will be the error message when it\'s exceeded.
        };

        //loads the UploadWidget-Dropzone
        new MMMediaBundleFileDropzone("#" + id, url, fieldName, multiple, files, _options);
    }

    MMMediaBundleFileDropzoneInitiateEvents();

};

/**
 * Start loading the dom is rdy
 */
MMMediaBundleDomReady(MMMediaBundleInit);<commit_msg>AdminMenu compatible<commit_after>/**
 * Created by tonigurski on 19.11.15.
 */

/**
 * preveneds Dropzone from autoloading
 * @type {boolean}
 */
Dropzone.autoDiscover = false;

/**
 * Helper to boot up the media bundle dropzone at the right moment
 */

(function (exports, d) {
    function MMMediaBundleDomReady(fn, context) {

        function onReady(event) {
            d.removeEventListener("DOMContentLoaded", onReady);
            fn.call(context || exports, event);
        }

        function onReadyIe(event) {
            if (d.readyState === "complete") {
                d.detachEvent("onreadystatechange", onReadyIe);
                fn.call(context || exports, event);
            }
        }

        d.addEventListener && d.addEventListener("DOMContentLoaded", onReady) ||
        d.attachEvent && d.attachEvent("onreadystatechange", onReadyIe);
    }

    exports.MMMediaBundleDomReady = MMMediaBundleDomReady;
})(window, document);

/**
 * Set ups a given dropzone to be compatible with the MMMediaBundleWidget
 *
 * @param _id
 * @param _url
 * @returns DropZone
 *
 * @constructor
 */
function MMMediaBundleFileDropzone(_id, _url, _fieldName, _multiple, _files, _options) {

    var $this = this;
    $this.id = _id;

    /**
     * creates and DOM input which holds a Media-Entity ID
     * @param _file
     * @returns {Element}
     */
    this.getHiddenEntityInput = function (_file) {

        if (!_file.id)
            return;

        var reference = document.createElement('input')
        reference.type = 'hidden';
        reference.name = _fieldName;
        reference.value = _file.id;

        return reference;
    };

    /**
     * appends an DOM input to the related previewElement of the given file-element
     * @param _file
     */
    this.appendHiddenEntityInput = function (_file) {

        if (_file) {

            var $reference = $this.getHiddenEntityInput(_file)

            if ($reference)
                _file.previewElement.appendChild($reference);
        }
    };

    /**
     * fires the afterInit event after the bundle finishs the custom init call
     */
    this.fireAfterInitEvent = function (_DropzoneDOM) {

        var $event;
        var $element = document.getElementById(this.id.replace('#', ''));

        if (document.createEvent) {
            $event = document.createEvent("HTMLEvents");

            $event.initEvent("MMMediaBundleFileDropzone.afterInit", true, true);
        } else {
            $event = document.createEventObject();
            $event.eventType = "MMMediaBundleFileDropzone.afterInit";
        }

        if (document.createEvent) {
            $element.dispatchEvent($event);
        } else {
            $element.fireEvent("on" + $event.eventType, $event);
        }
    };

    /**
     * function which get called by Dropzone after init
     */
    this.initCallback = function () {

        var myDropzone = this;

        /**
         * checks if its an mutiple field and adds the related event
         */
        if (_multiple) {

            /**
             * hooks the successmultiple event to append all hidden inputs which holds all tthe entity ids
             */
            myDropzone.on("successmultiple", function (files, response) {

                if (response && response.success) {

                    for (file in response.data) {

                        files[file].id = response.data[file].id;

                        $this.appendHiddenEntityInput(files[file]);

                    }

                }

            });
        }
        else {
            /**
             * hooks the sucess event to append a hidden input which holds the entity id
             */
            myDropzone.on("success", function (file, response) {

                if (response && response.success) {

                    for (tFile in response.data) {

                        file.id = response.data[0].id;

                        $this.appendHiddenEntityInput(file);
                    }
                }
            });
        }

        /**
         * add already stored files to the widget
         */

            //temporaty add this event to get the real formated dropzone-file objects
        myDropzone.on("addedfile", $this.appendHiddenEntityInput);

        //loops thought the presetted files and appends them to the dropzone_preview
        for (var i = 0; i < _files.length; i++) {
            var mock = _files[i];
            mock.accepted = true;

            myDropzone.files.push(mock);
            myDropzone.emit('addedfile', mock);

            myDropzone.createThumbnailFromUrl(mock, mock.url);
            myDropzone.emit('complete', mock);
        }

        //removes the temporaty added this event
        myDropzone.off("addedfile", $this.appendHiddenEntityInput);

        /**
         * loads Dragular to enable drag and drop functionality
         * @TODO: check if its possible to enable drag n drop with multiple instances of Dropzone
         */
        if (_multiple) {

            var $dragula = dragula([document.querySelector($id_dropzone_preview)],
                {
                    direction: 'horizontal',
                    mirrorContainer: document.querySelector($id_dropzone_preview)

                });
        }

        myDropzone.on("success", function (file, responseText) {
            myDropzone.responseText = responseText;
        });

        myDropzone.on("complete", function (file) {

            var tFile = file;
            var obj = null;

            /**
             * @Todo temporary fix for DropZone ...need to be removed if the file variable is fixed
             */
            setTimeout(function(){

                for (var i in myDropzone.responseText.data) {

                    if (myDropzone.responseText.data[i].id == tFile.id) {
                        obj = myDropzone.responseText.data[i];
                        break;
                    }
                }
                // Handle the responseText here. For example, add the text to the preview element:
                if (obj)
                    tFile.previewTemplate.querySelector('.dz-filename span').innerHTML = obj.path;
            },100);

        });

        $this.fireAfterInitEvent();

    };

    /**
     * dom id of preview container
     * @type {string}
     */
    var $id_dropzone_preview = _id + '_previews';

    var $options = {
        thumbnailWidth: 800,
        thumbnailHeight: 600,
        url: _url
        // The configuration we've talked about above
        , autoProcessQueue: true
        , paramName: (_multiple ? 'files' : 'files[]')
        , addRemoveLinks: true

        , previewsContainer: $id_dropzone_preview

        // multiple settings
        , uploadMultiple: _multiple
        , parallelUploads: ( (_multiple) ? 100 : 1 )
        , maxFiles: ( (_multiple) ? 100 : 1 )

        // The setting up of the dropzone
        , init: this.initCallback
    };

    /**
     * merge aditional given options
     */
    if (typeof _options == "object")
        for (var key in _options)
            $options[key] = _options[key];

    /**
     * loads the Dropzone instance
     */
    this.dropzone = new Dropzone(_id, $options);
}

function MMMediaBundleFileDropzoneInitiateEvents() {
    $.on('mmcmfadmin:tabs:add:first',MMMediaBundleInit);
}

var MMMediaBundleInit = function (event) {

    var elements = document.getElementsByClassName('mmmb-dropzone');

    for (var i = 0; i < elements.length; i++) {

        var dropzone = elements[i];

        var url = dropzone.getAttribute('data-url');
        var fieldName = dropzone.getAttribute('data-field-name');
        var id = dropzone.getAttribute('id');

        //fetches preloaded Files
        var files = dropzone.getAttribute('data-files');
        files = JSON.parse(files);

        // get the allowed filetypes
        var fileTypes = dropzone.getAttribute('data-accepted-files');
        fileTypes = fileTypes ? fileTypes : false;

        /*
         * @TODO: better bool check
         */
        var multiple = dropzone.getAttribute('data-multiple');

        if (multiple == "false" || multiple == "" || multiple == "0")
            multiple = false;
        else
            multiple = true;

        var _options = {

            acceptedFiles: fileTypes,

            dictDefaultMessage: dropzone.getAttribute('data-dictDefaultMessage'), // The message that gets displayed before any files are dropped. This is normally replaced by an image but defaults to "Drop files here to upload"'
            dictFallbackMessage: dropzone.getAttribute('data-dictFallbackMessage'), // If the browser is not supported, the default message will be replaced with this text. Defaults to "Your browser does not support drag'n'drop file uploads."'
            dictFallbackText: dropzone.getAttribute('data-dictFallbackText'), // This will be added before the file input files. If you provide a fallback element yourself, or if this option is null this will be ignored. Defaults to "Please use the fallback form below to upload your files like in the olden days."'
            dictInvalidFileType: dropzone.getAttribute('data-dictInvalidFileType'), // Shown as error message if the file doesn·'t match the file type.'
            dictFileTooBig: dropzone.getAttribute('data-dictFileTooBig'), // Shown when the file is too big. {{filesize}} and {{maxFilesize}} will be replaced.'
            dictResponseError: dropzone.getAttribute('data-dictResponseError'), // Shown as error message if the server response was invalid. {{statusCode}} will be replaced with the servers status code.'
            dictCancelUpload: dropzone.getAttribute('data-dictCancelUpload'), // If addRemoveLinks is true, the text to be used for the cancel upload link.'
            dictCancelUploadConfirmation: dropzone.getAttribute('data-dictCancelUploadConfirmation'), // If addRemoveLinks is true, the text to be used for confirmation when cancelling upload.'
            dictRemoveFile: dropzone.getAttribute('data-dictRemoveFile'), // If addRemoveLinks is true, the text to be used to remove a file.'
            dictRemoveFileConfirmation: dropzone.getAttribute('data-dictRemoveFileConfirmation'), //
            dictMaxFilesExceeded: dropzone.getAttribute('data-dictMaxFilesExceeded')  // If maxFiles is set, this will be the error message when it\'s exceeded.
        };

        //loads the UploadWidget-Dropzone
        new MMMediaBundleFileDropzone("#" + id, url, fieldName, multiple, files, _options);
    }

    MMMediaBundleFileDropzoneInitiateEvents();
};

/**
 * Start loading the dom is rdy
 */
MMMediaBundleDomReady(MMMediaBundleInit);

// CmfAdminBundle integration
<|endoftext|>