<commit_before>var Hoptoad = {
  VERSION           : '0.1.0',
  NOTICE_XML        : '<?xml version="1.0" encoding="UTF-8"?><notice version="2.0"><api-key></api-key><notifier><name>hoptoad_notifier_js</name><version>0.1.0</version><url>http://hoptoadapp.com</url></notifier><error><class>EXCEPTION_CLASS</class><message>EXCEPTION_MESSAGE</message><backtrace>BACKTRACE_LINES</backtrace></error><request><url>REQUEST_URL</url><component>REQUEST_COMPONENT</component><action>REQUEST_ACTION</action></request><server-environment><project-root>PROJECT_ROOT</project-root><environment-name>production</environment-name></server-environment></notice>',
  ROOT              : window.location.protocol + '//' + window.location.host,
  BACKTRACE_MATCHER : /^(.*)\@(.*)\:(\d+)$/,
  backtrace_filters : [/notifier\.js/],

  notify: function(error) {
    var xml     = escape(Hoptoad.generateXML(error));
    var host    = Hoptoad.host || 'hoptoadapp.com';
    var url     = '//' + host + '/notifier_api/v2/notices?data=' + xml;
    var request = document.createElement('iframe');

    request.style.width   = '1px';
    request.style.height  = '1px';
    request.style.display = 'none';
    request.src = url;

    document.getElementsByTagName('head')[0].appendChild(request);
  },

  setEnvironment: function(value) {
    var matcher = /<environment-name>.*<\/environment-name>/;

    Hoptoad.NOTICE_XML  = Hoptoad.NOTICE_XML.replace(matcher,
                                                     '<environment-name>' +
                                                       value +
                                                     '</environment-name>')
  },

  setHost: function(value) {
    Hoptoad.host = value;
  },

  setKey: function(value) {
    var matcher = /<api-key>.*<\/api-key>/;

    Hoptoad.NOTICE_XML = Hoptoad.NOTICE_XML.replace(matcher,
                                                    '<api-key>' +
                                                      value +
                                                    '</api-key>');
  },

  setErrorDefaults: function(value) {
    Hoptoad.errorDefaults = value;
  },

  generateXML: function(errorWithoutDefaults) {
    var error = Hoptoad.mergeDefault(Hoptoad.errorDefaults, errorWithoutDefaults);

    var xml       = Hoptoad.NOTICE_XML;
    var baseUrl   = error.url     || '';
    var hash      = location.hash || '';
    var url       = Hoptoad.escapeText((baseUrl + hash) || '');
    var component = Hoptoad.escapeText(error.component  || '');
    var action    = Hoptoad.escapeText(error.action     || '');
    var type      = Hoptoad.escapeText(error.type       || 'Error');
    var message   = Hoptoad.escapeText(error.message    || 'Unknown error.');
    var backtrace = Hoptoad.generateBacktrace(error);

    if (Hoptoad.trim(url) == '' && Hoptoad.trim(component) == '') {
      xml = xml.replace(/<request>.*<\/request>/, '');
    } else {
      var data    = '';

      var cgi_data = error['cgi-data'] || {};
      cgi_data["HTTP_USER_AGENT"] = navigator.userAgent;
      data += '<cgi-data>';
      data += Hoptoad.generateVariables(cgi_data);
      data += '</cgi-data>';

      var methods = ['params', 'session'];

      for (var i = 0; i < 2; i++) {
        var type = methods[i];

        if (error[type]) {
          data += '<' + type + '>';
          data += Hoptoad.generateVariables(error[type]);
          data += '</' + type + '>';
        }
      }

      xml = xml.replace('</request>',        data + '</request>')
               .replace('REQUEST_URL',       url)
               .replace('REQUEST_ACTION',    action)
               .replace('REQUEST_COMPONENT', component);
    }

    return xml.replace('PROJECT_ROOT',      Hoptoad.ROOT)
              .replace('EXCEPTION_CLASS',   type)
              .replace('EXCEPTION_MESSAGE', message)
              .replace('BACKTRACE_LINES',   backtrace.join(''));
  },

  generateBacktrace: function(error) {
    error = error || {};

    if (typeof error.stack != 'string') {
      try {
        (0)();
      } catch(e) {
        error.stack = e.stack;
      }
    }

    var backtrace  = [];
    var stacktrace = Hoptoad.getStackTrace(error);

    for (var i = 0, l = stacktrace.length; i < l; i++) {
      var line    = stacktrace[i];
      var matches = line.match(Hoptoad.BACKTRACE_MATCHER);

      if (matches && Hoptoad.validBacktraceLine(line)) {
        var file = matches[2].replace(Hoptoad.ROOT, '[PROJECT_ROOT]');

        if (i == 0) {
          if (matches[2].match(document.location.href)) {
            backtrace.push('<line method="" file="internal: " number=""/>');
          }
        }

        backtrace.push('<line method="' + Hoptoad.escapeText(matches[1]) +
                       '" file="' + Hoptoad.escapeText(file) +
                       '" number="' + matches[3] + '" />');
      }
    }

    return backtrace;
  },

  getStackTrace: function(error) {
    var stacktrace = printStackTrace({ e : error, guess : false });

    for (var i = 0, l = stacktrace.length; i < l; i++) {
      if (stacktrace[i].match(/\:\d+$/)) {
        continue;
      }

      if (stacktrace[i].indexOf('@') == -1) {
        stacktrace[i] += '@unsupported.js';
      }

      stacktrace[i] += ':0';
    }

    return stacktrace;
  },

  validBacktraceLine: function(line) {
    for (var i = 0; i < Hoptoad.backtrace_filters.length; i++) {
      if (line.match(Hoptoad.backtrace_filters[i])) {
        return false;
      }
    }

    return true;
  },

  generateVariables: function(parameters) {
    var key;
    var result = '';

    for (key in parameters) {
      result += '<var key="' + Hoptoad.escapeText(key) + '">' +
                  Hoptoad.escapeText(parameters[key]) +
                '</var>';
    }

    return result;
  },

  escapeText: function(text) {
    return text.replace(/&/g, '&')
               .replace(/</g, '<')
               .replace(/>/g, '>')
               .replace(/'/g, ''')
               .replace(/"/g, '"');
  },

  trim: function(text) {
    return text.toString().replace(/^\s+/, '').replace(/\s+$/, '');
  },

  mergeDefault: function(defaults, hash) {
    var cloned = {};
    var key;

    for (key in hash) {
      cloned[key] = hash[key];
    }

    for (key in defaults) {
      if (!cloned.hasOwnProperty(key)) {
        cloned[key] = defaults[key];
      }
    }

    return cloned;
  }
};

// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
//                  Luke Smith http://lucassmith.name/ (2008)
//                  Loic Dachary <loic@dachary.org> (2008)
//                  Johan Euphrosine <proppy@aminche.com> (2008)
//                  Øyvind Sean Kinsey http://kinsey.no/blog (2010)
//                  Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)
//
// Information and discussions
// http://jspoker.pokersource.info/skin/test-printstacktrace.html
// http://eriwen.com/javascript/js-stack-trace/
// http://eriwen.com/javascript/stacktrace-update/
// http://pastie.org/253058
//
// guessFunctionNameFromLines comes from firebug
//
// Software License Agreement (BSD License)
//
// Copyright (c) 2007, Parakey Inc.
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above
//   copyright notice, this list of conditions and the
//   following disclaimer.
//
// * Redistributions in binary form must reproduce the above
//   copyright notice, this list of conditions and the
//   following disclaimer in the documentation and/or other
//   materials provided with the distribution.
//
// * Neither the name of Parakey Inc. nor the names of its
//   contributors may be used to endorse or promote products
//   derived from this software without specific prior
//   written permission of Parakey Inc.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function printStackTrace(a){var a=a||{guess:!0},b=a.e||null,a=!!a.guess,c=new printStackTrace.implementation,b=c.run(b);return a?c.guessFunctions(b):b}printStackTrace.implementation=function(){};
printStackTrace.implementation.prototype={run:function(a){var a=a||this.createException(),b=this.mode(a);return b==="other"?this.other(arguments.callee):this[b](a)},createException:function(){try{return this.undef(),null}catch(a){return a}},mode:function(a){if(a.arguments&&a.stack)return this._mode="chrome";else if(typeof window!=="undefined"&&window.opera)return this._mode=a.stacktrace?"opera10":"opera";else if(a.stack)return this._mode="firefox";return this._mode="other"},instrumentFunction:function(a,
b,c){a=a||window;a["_old"+b]=a[b];a[b]=function(){c.call(this,printStackTrace());return a["_old"+b].apply(this,arguments)};a[b]._instrumented=!0},deinstrumentFunction:function(a,b){a[b].constructor===Function&&a[b]._instrumented&&a["_old"+b].constructor===Function&&(a[b]=a["_old"+b])},chrome:function(a){return a.stack.replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.<anonymous>\s*\(/gm,"{anonymous}()@").split("\n")},firefox:function(a){return a.stack.replace(/(?:\n@:0)?\s+$/m,
"").replace(/^\(/gm,"{anonymous}(").split("\n")},opera10:function(a){var a=a.stacktrace.split("\n"),b=/.*line (\d+), column (\d+) in ((<anonymous function\:?\s*(\S+))|([^\(]+)\([^\)]*\))(?: in )?(.*)\s*$/i,c,d,e;c=2;d=0;for(e=a.length;c<e-2;c++)if(b.test(a[c])){var g=RegExp.$6+":"+RegExp.$1+":"+RegExp.$2,f=RegExp.$3,f=f.replace(/<anonymous function\:?\s?(\S+)?>/g,"{anonymous}");a[d++]=f+"@"+g}a.splice(d,a.length-d);return a},opera:function(a){var a=a.message.split("\n"),b=/Line\s+(\d+).*script\s+(http\S+)(?:.*in\s+function\s+(\S+))?/i,
c,d,e;c=4;d=0;for(e=a.length;c<e;c+=2)b.test(a[c])&&(a[d++]=(RegExp.$3?RegExp.$3+"()@"+RegExp.$2+RegExp.$1:"{anonymous}()@"+RegExp.$2+":"+RegExp.$1)+" -- "+a[c+1].replace(/^\s+/,""));a.splice(d,a.length-d);return a},other:function(a){for(var b=/function\s*([\w\-$]+)?\s*\(/i,c=[],d,e;a&&c.length<10;)d=b.test(a.toString())?RegExp.$1||"{anonymous}":"{anonymous}",e=Array.prototype.slice.call(a.arguments||[]),c[c.length]=d+"("+this.stringifyArguments(e)+")",a=a.caller;return c},stringifyArguments:function(a){for(var b=
0;b<a.length;++b){var c=a[b];c===void 0?a[b]="undefined":c===null?a[b]="null":c.constructor&&(c.constructor===Array?a[b]=c.length<3?"["+this.stringifyArguments(c)+"]":"["+this.stringifyArguments(Array.prototype.slice.call(c,0,1))+"..."+this.stringifyArguments(Array.prototype.slice.call(c,-1))+"]":c.constructor===Object?a[b]="#object":c.constructor===Function?a[b]="#function":c.constructor===String&&(a[b]='"'+c+'"'))}return a.join(",")},sourceCache:{},ajax:function(a){var b=this.createXMLHTTPObject();
if(b)return b.open("GET",a,!1),b.setRequestHeader("User-Agent","XMLHTTP/1.0"),b.send(""),b.responseText},createXMLHTTPObject:function(){for(var a,b=[function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}],c=0;c<b.length;c++)try{return a=b[c](),this.createXMLHTTPObject=b[c],a}catch(d){}},isSameDomain:function(a){return a.indexOf(location.hostname)!==
-1},getSource:function(a){a in this.sourceCache||(this.sourceCache[a]=this.ajax(a).split("\n"));return this.sourceCache[a]},guessFunctions:function(a){for(var b=0;b<a.length;++b){var c=a[b],d=/\{anonymous\}\(.*\)@(\w+:\/\/([\-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/.exec(c);if(d){var e=d[1],d=d[4];e&&this.isSameDomain(e)&&d&&(e=this.guessFunctionName(e,d),a[b]=c.replace("{anonymous}",e))}}return a},guessFunctionName:function(a,b){var c;try{c=this.guessFunctionNameFromLines(b,this.getSource(a))}catch(d){c=
"getSource failed with url: "+a+", exception: "+d.toString()}return c},guessFunctionNameFromLines:function(a,b){for(var c=/function ([^(]*)\(([^)]*)\)/,d=/['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,e="",g=0;g<10;++g)if(e=b[a-g]+e,e!==void 0){var f=d.exec(e);if(f&&f[1])return f[1];else if((f=c.exec(e))&&f[1])return f[1]}return"(?)"}};

window.onerror = function(message, file, line) {
  setTimeout(function() {
    Hoptoad.notify({
      message   : message,
      url       : window.location.href,
      type      : message.constructor.name,
      stack     : '()@' + file + ':' + line
    });
  }, 100);
  return false;
};
<commit_msg>don't die inside of escapeText<commit_after>var Hoptoad = {
  VERSION           : '0.1.0',
  NOTICE_XML        : '<?xml version="1.0" encoding="UTF-8"?><notice version="2.0"><api-key></api-key><notifier><name>hoptoad_notifier_js</name><version>0.1.0</version><url>http://hoptoadapp.com</url></notifier><error><class>EXCEPTION_CLASS</class><message>EXCEPTION_MESSAGE</message><backtrace>BACKTRACE_LINES</backtrace></error><request><url>REQUEST_URL</url><component>REQUEST_COMPONENT</component><action>REQUEST_ACTION</action></request><server-environment><project-root>PROJECT_ROOT</project-root><environment-name>production</environment-name></server-environment></notice>',
  ROOT              : window.location.protocol + '//' + window.location.host,
  BACKTRACE_MATCHER : /^(.*)\@(.*)\:(\d+)$/,
  backtrace_filters : [/notifier\.js/],

  notify: function(error) {
    var xml     = escape(Hoptoad.generateXML(error));
    var host    = Hoptoad.host || 'hoptoadapp.com';
    var url     = '//' + host + '/notifier_api/v2/notices?data=' + xml;
    var request = document.createElement('iframe');

    request.style.width   = '1px';
    request.style.height  = '1px';
    request.style.display = 'none';
    request.src = url;

    document.getElementsByTagName('head')[0].appendChild(request);
  },

  setEnvironment: function(value) {
    var matcher = /<environment-name>.*<\/environment-name>/;

    Hoptoad.NOTICE_XML  = Hoptoad.NOTICE_XML.replace(matcher,
                                                     '<environment-name>' +
                                                       value +
                                                     '</environment-name>')
  },

  setHost: function(value) {
    Hoptoad.host = value;
  },

  setKey: function(value) {
    var matcher = /<api-key>.*<\/api-key>/;

    Hoptoad.NOTICE_XML = Hoptoad.NOTICE_XML.replace(matcher,
                                                    '<api-key>' +
                                                      value +
                                                    '</api-key>');
  },

  setErrorDefaults: function(value) {
    Hoptoad.errorDefaults = value;
  },

  generateXML: function(errorWithoutDefaults) {
    var error = Hoptoad.mergeDefault(Hoptoad.errorDefaults, errorWithoutDefaults);

    var xml       = Hoptoad.NOTICE_XML;
    var baseUrl   = error.url     || '';
    var hash      = location.hash || '';
    var url       = Hoptoad.escapeText((baseUrl + hash) || '');
    var component = Hoptoad.escapeText(error.component  || '');
    var action    = Hoptoad.escapeText(error.action     || '');
    var type      = Hoptoad.escapeText(error.type       || 'Error');
    var message   = Hoptoad.escapeText(error.message    || 'Unknown error.');
    var backtrace = Hoptoad.generateBacktrace(error);

    if (Hoptoad.trim(url) == '' && Hoptoad.trim(component) == '') {
      xml = xml.replace(/<request>.*<\/request>/, '');
    } else {
      var data    = '';

      var cgi_data = error['cgi-data'] || {};
      cgi_data["HTTP_USER_AGENT"] = navigator.userAgent;
      data += '<cgi-data>';
      data += Hoptoad.generateVariables(cgi_data);
      data += '</cgi-data>';

      var methods = ['params', 'session'];

      for (var i = 0; i < 2; i++) {
        var type = methods[i];

        if (error[type]) {
          data += '<' + type + '>';
          data += Hoptoad.generateVariables(error[type]);
          data += '</' + type + '>';
        }
      }

      xml = xml.replace('</request>',        data + '</request>')
               .replace('REQUEST_URL',       url)
               .replace('REQUEST_ACTION',    action)
               .replace('REQUEST_COMPONENT', component);
    }

    return xml.replace('PROJECT_ROOT',      Hoptoad.ROOT)
              .replace('EXCEPTION_CLASS',   type)
              .replace('EXCEPTION_MESSAGE', message)
              .replace('BACKTRACE_LINES',   backtrace.join(''));
  },

  generateBacktrace: function(error) {
    error = error || {};

    if (typeof error.stack != 'string') {
      try {
        (0)();
      } catch(e) {
        error.stack = e.stack;
      }
    }

    var backtrace  = [];
    var stacktrace = Hoptoad.getStackTrace(error);

    for (var i = 0, l = stacktrace.length; i < l; i++) {
      var line    = stacktrace[i];
      var matches = line.match(Hoptoad.BACKTRACE_MATCHER);

      if (matches && Hoptoad.validBacktraceLine(line)) {
        var file = matches[2].replace(Hoptoad.ROOT, '[PROJECT_ROOT]');

        if (i == 0) {
          if (matches[2].match(document.location.href)) {
            backtrace.push('<line method="" file="internal: " number=""/>');
          }
        }

        backtrace.push('<line method="' + Hoptoad.escapeText(matches[1]) +
                       '" file="' + Hoptoad.escapeText(file) +
                       '" number="' + matches[3] + '" />');
      }
    }

    return backtrace;
  },

  getStackTrace: function(error) {
    var stacktrace = printStackTrace({ e : error, guess : false });

    for (var i = 0, l = stacktrace.length; i < l; i++) {
      if (stacktrace[i].match(/\:\d+$/)) {
        continue;
      }

      if (stacktrace[i].indexOf('@') == -1) {
        stacktrace[i] += '@unsupported.js';
      }

      stacktrace[i] += ':0';
    }

    return stacktrace;
  },

  validBacktraceLine: function(line) {
    for (var i = 0; i < Hoptoad.backtrace_filters.length; i++) {
      if (line.match(Hoptoad.backtrace_filters[i])) {
        return false;
      }
    }

    return true;
  },

  generateVariables: function(parameters) {
    var key;
    var result = '';

    for (key in parameters) {
      result += '<var key="' + Hoptoad.escapeText(key) + '">' +
                  Hoptoad.escapeText(parameters[key]) +
                '</var>';
    }

    return result;
  },

  escapeText: function(text) {
    if (typeof(text) !== 'string' && text.toString)
      text = text.toString();
    return text.replace(/&/g, '&')
               .replace(/</g, '<')
               .replace(/>/g, '>')
               .replace(/'/g, ''')
               .replace(/"/g, '"');
  },

  trim: function(text) {
    return text.toString().replace(/^\s+/, '').replace(/\s+$/, '');
  },

  mergeDefault: function(defaults, hash) {
    var cloned = {};
    var key;

    for (key in hash) {
      cloned[key] = hash[key];
    }

    for (key in defaults) {
      if (!cloned.hasOwnProperty(key)) {
        cloned[key] = defaults[key];
      }
    }

    return cloned;
  }
};

// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
//                  Luke Smith http://lucassmith.name/ (2008)
//                  Loic Dachary <loic@dachary.org> (2008)
//                  Johan Euphrosine <proppy@aminche.com> (2008)
//                  Øyvind Sean Kinsey http://kinsey.no/blog (2010)
//                  Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)
//
// Information and discussions
// http://jspoker.pokersource.info/skin/test-printstacktrace.html
// http://eriwen.com/javascript/js-stack-trace/
// http://eriwen.com/javascript/stacktrace-update/
// http://pastie.org/253058
//
// guessFunctionNameFromLines comes from firebug
//
// Software License Agreement (BSD License)
//
// Copyright (c) 2007, Parakey Inc.
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above
//   copyright notice, this list of conditions and the
//   following disclaimer.
//
// * Redistributions in binary form must reproduce the above
//   copyright notice, this list of conditions and the
//   following disclaimer in the documentation and/or other
//   materials provided with the distribution.
//
// * Neither the name of Parakey Inc. nor the names of its
//   contributors may be used to endorse or promote products
//   derived from this software without specific prior
//   written permission of Parakey Inc.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function printStackTrace(a){var a=a||{guess:!0},b=a.e||null,a=!!a.guess,c=new printStackTrace.implementation,b=c.run(b);return a?c.guessFunctions(b):b}printStackTrace.implementation=function(){};
printStackTrace.implementation.prototype={run:function(a){var a=a||this.createException(),b=this.mode(a);return b==="other"?this.other(arguments.callee):this[b](a)},createException:function(){try{return this.undef(),null}catch(a){return a}},mode:function(a){if(a.arguments&&a.stack)return this._mode="chrome";else if(typeof window!=="undefined"&&window.opera)return this._mode=a.stacktrace?"opera10":"opera";else if(a.stack)return this._mode="firefox";return this._mode="other"},instrumentFunction:function(a,
b,c){a=a||window;a["_old"+b]=a[b];a[b]=function(){c.call(this,printStackTrace());return a["_old"+b].apply(this,arguments)};a[b]._instrumented=!0},deinstrumentFunction:function(a,b){a[b].constructor===Function&&a[b]._instrumented&&a["_old"+b].constructor===Function&&(a[b]=a["_old"+b])},chrome:function(a){return a.stack.replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.<anonymous>\s*\(/gm,"{anonymous}()@").split("\n")},firefox:function(a){return a.stack.replace(/(?:\n@:0)?\s+$/m,
"").replace(/^\(/gm,"{anonymous}(").split("\n")},opera10:function(a){var a=a.stacktrace.split("\n"),b=/.*line (\d+), column (\d+) in ((<anonymous function\:?\s*(\S+))|([^\(]+)\([^\)]*\))(?: in )?(.*)\s*$/i,c,d,e;c=2;d=0;for(e=a.length;c<e-2;c++)if(b.test(a[c])){var g=RegExp.$6+":"+RegExp.$1+":"+RegExp.$2,f=RegExp.$3,f=f.replace(/<anonymous function\:?\s?(\S+)?>/g,"{anonymous}");a[d++]=f+"@"+g}a.splice(d,a.length-d);return a},opera:function(a){var a=a.message.split("\n"),b=/Line\s+(\d+).*script\s+(http\S+)(?:.*in\s+function\s+(\S+))?/i,
c,d,e;c=4;d=0;for(e=a.length;c<e;c+=2)b.test(a[c])&&(a[d++]=(RegExp.$3?RegExp.$3+"()@"+RegExp.$2+RegExp.$1:"{anonymous}()@"+RegExp.$2+":"+RegExp.$1)+" -- "+a[c+1].replace(/^\s+/,""));a.splice(d,a.length-d);return a},other:function(a){for(var b=/function\s*([\w\-$]+)?\s*\(/i,c=[],d,e;a&&c.length<10;)d=b.test(a.toString())?RegExp.$1||"{anonymous}":"{anonymous}",e=Array.prototype.slice.call(a.arguments||[]),c[c.length]=d+"("+this.stringifyArguments(e)+")",a=a.caller;return c},stringifyArguments:function(a){for(var b=
0;b<a.length;++b){var c=a[b];c===void 0?a[b]="undefined":c===null?a[b]="null":c.constructor&&(c.constructor===Array?a[b]=c.length<3?"["+this.stringifyArguments(c)+"]":"["+this.stringifyArguments(Array.prototype.slice.call(c,0,1))+"..."+this.stringifyArguments(Array.prototype.slice.call(c,-1))+"]":c.constructor===Object?a[b]="#object":c.constructor===Function?a[b]="#function":c.constructor===String&&(a[b]='"'+c+'"'))}return a.join(",")},sourceCache:{},ajax:function(a){var b=this.createXMLHTTPObject();
if(b)return b.open("GET",a,!1),b.setRequestHeader("User-Agent","XMLHTTP/1.0"),b.send(""),b.responseText},createXMLHTTPObject:function(){for(var a,b=[function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}],c=0;c<b.length;c++)try{return a=b[c](),this.createXMLHTTPObject=b[c],a}catch(d){}},isSameDomain:function(a){return a.indexOf(location.hostname)!==
-1},getSource:function(a){a in this.sourceCache||(this.sourceCache[a]=this.ajax(a).split("\n"));return this.sourceCache[a]},guessFunctions:function(a){for(var b=0;b<a.length;++b){var c=a[b],d=/\{anonymous\}\(.*\)@(\w+:\/\/([\-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/.exec(c);if(d){var e=d[1],d=d[4];e&&this.isSameDomain(e)&&d&&(e=this.guessFunctionName(e,d),a[b]=c.replace("{anonymous}",e))}}return a},guessFunctionName:function(a,b){var c;try{c=this.guessFunctionNameFromLines(b,this.getSource(a))}catch(d){c=
"getSource failed with url: "+a+", exception: "+d.toString()}return c},guessFunctionNameFromLines:function(a,b){for(var c=/function ([^(]*)\(([^)]*)\)/,d=/['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,e="",g=0;g<10;++g)if(e=b[a-g]+e,e!==void 0){var f=d.exec(e);if(f&&f[1])return f[1];else if((f=c.exec(e))&&f[1])return f[1]}return"(?)"}};

window.onerror = function(message, file, line) {
  setTimeout(function() {
    Hoptoad.notify({
      message   : message,
      url       : window.location.href,
      type      : message.constructor.name,
      stack     : '()@' + file + ':' + line
    });
  }, 100);
  return false;
};
<|endoftext|>


<commit_before>import React from 'react';
import BudgetItem from '../components/BudgetItem'
import CheckinAccounts from '../components/CheckinAccounts'

function BudgetList(props) {
    var budgetItems = props.budgetItems;
    var year = props.year;
    var month = props.month;

    return (

                    <button className="ui button">

                    </button>
                    <button className="ui button blue">
                        {year} {month}
                    </button>
                    <button className="ui button">

                    </button>

                    <CheckinAccounts />

                {budgetItems.map(function(budgetItem) {
                  return

                        <BudgetItem budgetItem={budgetItem} />

                })}

    );
}

export default BudgetList;
<commit_msg>added new buttons: AddCategory and AddTransaction<commit_after>import React from 'react';
import BudgetItem from '../components/BudgetItem'
import CheckinAccounts from '../components/CheckinAccounts'

function BudgetList(props) {
    var budgetItems = props.budgetItems;
    var year = props.year;
    var month = props.month;

    return (

                    <button className="ui button">

                    </button>
                    <button className="ui button blue">
                        {year} {month}
                    </button>
                    <button className="ui button">

                    </button>

                    <CheckinAccounts />

                {budgetItems.map(function(budgetItem) {
                  return

                        <BudgetItem budgetItem={budgetItem} />

                })}

              <button className="ui button blue">Add Category</button>
              <button className="ui button blue">Add Transaction</button>

    );
}

export default BudgetList;
<|endoftext|>


<commit_before>/*!
 * js-comments-template <https://github.com/jonschlinkert/js-comments-template>
 *
 * Copyright (c) 2014-2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

module.exports = [
  // ## title
  '<% if (comment.title) { %>' + include('title') + '<% } %>',

  // Description
  '<%= comment.description %>',

  // **params**
  '<% if (comment.params) { %>' + include('params') + '<% } %>',
  // **returns**
  '<% if (comment.returns) { %>' + include('returns') + '<% } %>',
  // **events**
  '<% if (comment.emits) { %>' + include('emits') + '<% } %>',
  // **examples**
  '<% if (comment.examples.length) { %>' + include('examples') + '<% } %>'
].join('\n');

function include(name) {
  return require(__dirname + '/' + name + '.js');
}
<commit_msg>put examples before params<commit_after>/*!
 * js-comments-template <https://github.com/jonschlinkert/js-comments-template>
 *
 * Copyright (c) 2014-2015, Jon Schlinkert.
 * Licensed under the MIT License.
 */

module.exports = [
  // ## title
  '<% if (comment.title) { %>' + include('title') + '<% } %>',

  // Description
  '<%= comment.description %>',

  // **examples**
  '<% if (comment.examples.length) { %>' + include('examples') + '<% } %>',

  // **params**
  '<% if (comment.params) { %>' + include('params') + '<% } %>',
  // **returns**
  '<% if (comment.returns) { %>' + include('returns') + '<% } %>',
  // **events**
  '<% if (comment.emits) { %>' + include('emits') + '<% } %>',
].join('\n');

function include(name) {
  return require(__dirname + '/' + name + '.js');
}
<|endoftext|>


<commit_before>/* jshint latedef:nofunc */
'use strict';

var isNumber = require('lodash/isNumber');
var AnnotatedTextComponent = require('./AnnotatedTextComponent');
var Component = require('./Component');
var $$ = Component.$$;
var Coordinate = require('../model/Coordinate');

/**
  Renders a text property. Used internally by different components to render editable text.

  @class
  @component
  @extends ui/AnnotatedTextComponent

  @prop {String[]} path path to a text property
  @prop {String} [tagName] specifies which tag should be used - defaults to `div`

  @example

  ```js
  $$(TextProperty, {
    path: [ 'paragraph-1', 'content']
  })
  ```
*/

function TextPropertyComponent() {
  TextPropertyComponent.super.apply(this, arguments);
}

TextPropertyComponent.Prototype = function() {

  var _super = Object.getPrototypeOf(this);

  this.render = function() {
    var path = this.props.path;

    var el = this._renderContent()
      .addClass('sc-text-property')
      .attr({
        "data-path": path.join('.'),
        spellCheck: false,
      })
      .css({
        whiteSpace: "pre-wrap"
      });
    el.append($$('br'));
    return el;
  };

  this._renderFragment = function(fragment) {
    var node = fragment.node;
    var id = node.id;
    var el;
    if (node.type === 'cursor' || node.type === 'selection-fragment') {
      el = $$('span').addClass('se-'+node.type);
      if (node.userData) {
        el.addClass('sm-user-'+node.userData.sessionIndex);
      } else {
        el.addClass('sm-local-user');
      }
    } else {
      el = _super._renderFragment.call(this, fragment);
      if (node.constructor.static.isInline) {
        el.attr({
          'contentEditable': false,
          'data-inline':'1',
          'data-length': 1
        });
      }
      // adding refs here, enables preservative rerendering
      // TODO: while this solves problems with rerendering inline nodes
      // with external content, it decreases the overall performance too much.
      // We should optimize the component first before we can enable this.
      if (this.context.config && this.context.config.preservativeTextPropertyRendering) {
        el.ref(id + "@" + fragment.counter);
      }
    }
    el.attr('data-offset', fragment.pos);
    return el;
  };

  this._finishFragment = function(fragment, context, parentContext) {
    context.attr('data-length', fragment.length);
    parentContext.append(context);
  };

  this.didMount = function() {
    var surface = this.getSurface();
    if (surface) {
      surface._registerTextProperty(this.props.path, this);
    }
  };

  this.dispose = function() {
    _super.dispose.call(this);
    var surface = this.getSurface();
    if (surface) {
      surface._unregisterTextProperty(this.props.path, this);
    }
  };

  this.getText = function() {
    return this.getDocument().get(this.props.path);
  };

  this.getAnnotations = function() {
    var annotations = this.getDocument().getIndex('annotations').get(this.props.path);
    var surface = this.getSurface();
    if (surface) {
      var fragments = surface._getFragments(this.props.path);
      if (fragments) {
        annotations = annotations.concat(fragments);
      }
    }
    return annotations;
  };

  this.setFragments = function() {
    this.children[0].extendProps({ annotations: this.getAnnotations() });
  };

  this.getDocument = function() {
    return this.props.doc ||this.context.doc;
  };

  this.getController = function() {
    return this.props.controller || this.context.controller;
  };

  this.getSurface = function() {
    return this.props.surface ||this.context.surface;
  };

  this.isEditable = function() {
    return this.getSurface().isEditable();
  };

  this.isReadonly = function() {
    return this.getSurface().isReadonly();
  };

  this.getDOMCoordinate = function(charPos) {
    return this._getDOMCoordinate(this.el, charPos);
  };

  this._getDOMCoordinate = function(el, charPos) {
    var l;
    var idx = 0;
    if (charPos === 0) {
      return {
        container: el,
        offset: 0
      };
    }
    for (var child = el.firstChild; child; child=child.nextSibling, idx++) {
      if (child.nodeType === 3) {
        l = child.length;
        if (l >= charPos) {
          return {
            container: child,
            offset: charPos
          };
        } else {
          charPos -= l;
        }
      } else if (child.nodeType === 1) {
        if (child.dataset && child.dataset.length) {
          l = parseInt(child.dataset.length, 10);
          if (l>= charPos) {
            return this._getDOMCoordinate(child, charPos, idx);
          } else {
            charPos -= l;
          }
        } else {
          console.error('FIXME: Can not map to DOM coordinates.');
          return null;
        }
      }
    }
  };

};

AnnotatedTextComponent.extend(TextPropertyComponent);

// Helpers for DOM selection mapping

TextPropertyComponent.getCoordinate = function(root, node, offset) {
  var context = _getPropertyContext(root, node, offset);
  if (!context) {
    return null;
  }
  // in some cases we need to normalize the DOM coordinate
  // before we can use it for retrieving charPos
  // E.g. observed with #273
  node = context.node;
  offset = context.offset;
  var charPos = _getCharPos(context.node, context.offset);
  if (isNumber(charPos)) {
    return new Coordinate(context.path, charPos);
  }
  return null;
};

function _getPropertyContext(root, node, offset) {
  var result = {
    el: null,
    path: null,
    node: node,
    offset: offset
  };
  while (true) {
    if (!node || node === root) {
      return null;
    }
    if (node.nodeType === 1) {
      if (node.dataset && node.dataset.path) {
        result.el = node;
        result.path = node.dataset.path.split('.');
        return result;
      }
      if (node.dataset && node.dataset.inline) {
        // we need to normalize situations where the DOM coordinate
        // is inside an inline node, which we have observed
        // can actually happen.
        result.node = node;
        if (offset > 0) {
          result.offset = 1;
        }
      }
    }
    node = node.parentNode;
  }
  return null;
}

function _getCharPos(node, offset) {
  var charPos = offset;
  var parent, childIdx;

  /*
    In the following implementation we are exploiting two facts
    for optimization:
    - an element with data-path is assumed to be the text property element
    - an element with data-offset is assumed to be an annotation element

    Particularly, the data-offset property is helpful to get the character position
    in just one iteration.
  */

  parent = node.parentNode;
  if (node.nodeType === 3) {
    // TextNode is first child
    if (node === parent.firstChild) {
      // ... we can stop if parent is text property
      if (parent.dataset.path) {
        charPos = offset;
      }
      // ... and we can stop if parent has an offset hint
      else if (parent.dataset.offset) {
        charPos = parseInt(parent.dataset.offset, 10) + offset;
      }
      // ... otherwise we count the charPos by recursing up-tree
      else {
        charPos = _getCharPos(parent, 0) + offset;
      }
    } else {
      // the node has a predecessor so we can apply recurse using the child index
      childIdx = Array.prototype.indexOf.call(parent.childNodes, node);
      charPos = _getCharPos(parent, childIdx) + offset;
    }
  } else if (node.nodeType === 1) {
    var dataset = node.dataset;
    // if node is the element of a text property, then offset is a child index
    // up to which we need to sum up all lengths
    if (dataset && dataset.path) {
      charPos = _countCharacters(node, offset);
    }
    // similar if node is the element of an annotation, and we can use the
    // element's offset
    else if (dataset && dataset.offset) {
      childIdx = Array.prototype.indexOf.call(parent.childNodes, node);
      charPos = parseInt(dataset.offset, 10) + _countCharacters(node, offset);
    }
    // for other elements we need to count characters in the child tree
    // adding the offset of this element which needs to be computed by recursing up-tree
    else {
      childIdx = Array.prototype.indexOf.call(parent.childNodes, node);
      charPos = _getCharPos(parent, childIdx) + _countCharacters(node, offset);
    }
  } else {
    // Unsupported case
    return null;
  }
  return charPos;
}

function _countCharacters(el, maxIdx) {
  var charPos = 0;
  // inline elements have a length of 1
  if (el.dataset && el.dataset.inline) {
    return maxIdx === 0 ? 0 : 1;
  }
  var l = el.childNodes.length;
  if (arguments.length === 1) {
    maxIdx = l;
  }
  maxIdx = Math.min(l, maxIdx);
  for (var i=0, child = el.firstChild; i < maxIdx; child = child.nextSibling, i++) {
    if (child.nodeType === 3) {
      charPos += child.length;
    } else if (child.nodeType === 1) {
      var dataset = child.dataset;
      if (dataset && dataset.inline) {
        charPos += 1;
      } else if (dataset && dataset.length) {
        charPos += parseInt(dataset.length, 10);
      } else {
        charPos += _countCharacters(child);
      }
    }
  }
  return charPos;
}

module.exports = TextPropertyComponent;
<commit_msg>Add the contenteditable attribute when the TextPropertyEditor is editable<commit_after>/* jshint latedef:nofunc */
'use strict';

var isNumber = require('lodash/isNumber');
var AnnotatedTextComponent = require('./AnnotatedTextComponent');
var Component = require('./Component');
var $$ = Component.$$;
var Coordinate = require('../model/Coordinate');

/**
  Renders a text property. Used internally by different components to render editable text.

  @class
  @component
  @extends ui/AnnotatedTextComponent

  @prop {String[]} path path to a text property
  @prop {String} [tagName] specifies which tag should be used - defaults to `div`

  @example

  ```js
  $$(TextProperty, {
    path: [ 'paragraph-1', 'content']
  })
  ```
*/

function TextPropertyComponent() {
  TextPropertyComponent.super.apply(this, arguments);
}

TextPropertyComponent.Prototype = function() {

  var _super = Object.getPrototypeOf(this);

  this.render = function() {
    var path = this.props.path;

    var isEditable = this.isEditable();

    var el = this._renderContent()
      .addClass('sc-text-property')
      .attr({
        "data-path": path.join('.'),
        spellCheck: false,
        contenteditable: isEditable
      })
      .css({
        whiteSpace: "pre-wrap"
      });
    el.append($$('br'));
    return el;
  };

  this._renderFragment = function(fragment) {
    var node = fragment.node;
    var id = node.id;
    var el;
    if (node.type === 'cursor' || node.type === 'selection-fragment') {
      el = $$('span').addClass('se-'+node.type);
      if (node.userData) {
        el.addClass('sm-user-'+node.userData.sessionIndex);
      } else {
        el.addClass('sm-local-user');
      }
    } else {
      el = _super._renderFragment.call(this, fragment);
      if (node.constructor.static.isInline) {
        el.attr({
          'contentEditable': false,
          'data-inline':'1',
          'data-length': 1
        });
      }
      // adding refs here, enables preservative rerendering
      // TODO: while this solves problems with rerendering inline nodes
      // with external content, it decreases the overall performance too much.
      // We should optimize the component first before we can enable this.
      if (this.context.config && this.context.config.preservativeTextPropertyRendering) {
        el.ref(id + "@" + fragment.counter);
      }
    }
    el.attr('data-offset', fragment.pos);
    return el;
  };

  this._finishFragment = function(fragment, context, parentContext) {
    context.attr('data-length', fragment.length);
    parentContext.append(context);
  };

  this.didMount = function() {
    var surface = this.getSurface();
    if (surface) {
      surface._registerTextProperty(this.props.path, this);
    }
  };

  this.dispose = function() {
    _super.dispose.call(this);
    var surface = this.getSurface();
    if (surface) {
      surface._unregisterTextProperty(this.props.path, this);
    }
  };

  this.getText = function() {
    return this.getDocument().get(this.props.path);
  };

  this.getAnnotations = function() {
    var annotations = this.getDocument().getIndex('annotations').get(this.props.path);
    var surface = this.getSurface();
    if (surface) {
      var fragments = surface._getFragments(this.props.path);
      if (fragments) {
        annotations = annotations.concat(fragments);
      }
    }
    return annotations;
  };

  this.setFragments = function() {
    this.children[0].extendProps({ annotations: this.getAnnotations() });
  };

  this.getDocument = function() {
    return this.props.doc ||this.context.doc;
  };

  this.getController = function() {
    return this.props.controller || this.context.controller;
  };

  this.getSurface = function() {
    return this.props.surface ||this.context.surface;
  };

  this.isEditable = function() {
    return this.getSurface().isEditable();
  };

  this.isReadonly = function() {
    return this.getSurface().isReadonly();
  };

  this.getDOMCoordinate = function(charPos) {
    return this._getDOMCoordinate(this.el, charPos);
  };

  this._getDOMCoordinate = function(el, charPos) {
    var l;
    var idx = 0;
    if (charPos === 0) {
      return {
        container: el,
        offset: 0
      };
    }
    for (var child = el.firstChild; child; child=child.nextSibling, idx++) {
      if (child.nodeType === 3) {
        l = child.length;
        if (l >= charPos) {
          return {
            container: child,
            offset: charPos
          };
        } else {
          charPos -= l;
        }
      } else if (child.nodeType === 1) {
        if (child.dataset && child.dataset.length) {
          l = parseInt(child.dataset.length, 10);
          if (l>= charPos) {
            return this._getDOMCoordinate(child, charPos, idx);
          } else {
            charPos -= l;
          }
        } else {
          console.error('FIXME: Can not map to DOM coordinates.');
          return null;
        }
      }
    }
  };

};

AnnotatedTextComponent.extend(TextPropertyComponent);

// Helpers for DOM selection mapping

TextPropertyComponent.getCoordinate = function(root, node, offset) {
  var context = _getPropertyContext(root, node, offset);
  if (!context) {
    return null;
  }
  // in some cases we need to normalize the DOM coordinate
  // before we can use it for retrieving charPos
  // E.g. observed with #273
  node = context.node;
  offset = context.offset;
  var charPos = _getCharPos(context.node, context.offset);
  if (isNumber(charPos)) {
    return new Coordinate(context.path, charPos);
  }
  return null;
};

function _getPropertyContext(root, node, offset) {
  var result = {
    el: null,
    path: null,
    node: node,
    offset: offset
  };
  while (true) {
    if (!node || node === root) {
      return null;
    }
    if (node.nodeType === 1) {
      if (node.dataset && node.dataset.path) {
        result.el = node;
        result.path = node.dataset.path.split('.');
        return result;
      }
      if (node.dataset && node.dataset.inline) {
        // we need to normalize situations where the DOM coordinate
        // is inside an inline node, which we have observed
        // can actually happen.
        result.node = node;
        if (offset > 0) {
          result.offset = 1;
        }
      }
    }
    node = node.parentNode;
  }
  return null;
}

function _getCharPos(node, offset) {
  var charPos = offset;
  var parent, childIdx;

  /*
    In the following implementation we are exploiting two facts
    for optimization:
    - an element with data-path is assumed to be the text property element
    - an element with data-offset is assumed to be an annotation element

    Particularly, the data-offset property is helpful to get the character position
    in just one iteration.
  */

  parent = node.parentNode;
  if (node.nodeType === 3) {
    // TextNode is first child
    if (node === parent.firstChild) {
      // ... we can stop if parent is text property
      if (parent.dataset.path) {
        charPos = offset;
      }
      // ... and we can stop if parent has an offset hint
      else if (parent.dataset.offset) {
        charPos = parseInt(parent.dataset.offset, 10) + offset;
      }
      // ... otherwise we count the charPos by recursing up-tree
      else {
        charPos = _getCharPos(parent, 0) + offset;
      }
    } else {
      // the node has a predecessor so we can apply recurse using the child index
      childIdx = Array.prototype.indexOf.call(parent.childNodes, node);
      charPos = _getCharPos(parent, childIdx) + offset;
    }
  } else if (node.nodeType === 1) {
    var dataset = node.dataset;
    // if node is the element of a text property, then offset is a child index
    // up to which we need to sum up all lengths
    if (dataset && dataset.path) {
      charPos = _countCharacters(node, offset);
    }
    // similar if node is the element of an annotation, and we can use the
    // element's offset
    else if (dataset && dataset.offset) {
      childIdx = Array.prototype.indexOf.call(parent.childNodes, node);
      charPos = parseInt(dataset.offset, 10) + _countCharacters(node, offset);
    }
    // for other elements we need to count characters in the child tree
    // adding the offset of this element which needs to be computed by recursing up-tree
    else {
      childIdx = Array.prototype.indexOf.call(parent.childNodes, node);
      charPos = _getCharPos(parent, childIdx) + _countCharacters(node, offset);
    }
  } else {
    // Unsupported case
    return null;
  }
  return charPos;
}

function _countCharacters(el, maxIdx) {
  var charPos = 0;
  // inline elements have a length of 1
  if (el.dataset && el.dataset.inline) {
    return maxIdx === 0 ? 0 : 1;
  }
  var l = el.childNodes.length;
  if (arguments.length === 1) {
    maxIdx = l;
  }
  maxIdx = Math.min(l, maxIdx);
  for (var i=0, child = el.firstChild; i < maxIdx; child = child.nextSibling, i++) {
    if (child.nodeType === 3) {
      charPos += child.length;
    } else if (child.nodeType === 1) {
      var dataset = child.dataset;
      if (dataset && dataset.inline) {
        charPos += 1;
      } else if (dataset && dataset.length) {
        charPos += parseInt(dataset.length, 10);
      } else {
        charPos += _countCharacters(child);
      }
    }
  }
  return charPos;
}

module.exports = TextPropertyComponent;
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
'use strict';

const resolvePlugins = require('../lib/resolvePlugins');
const resolvePlugin = resolvePlugins.resolvePlugin;

const defaultPlugins = resolvePlugins([
  'syntax-class-properties',
  'syntax-trailing-function-commas',
  'transform-class-properties',
  'transform-es2015-block-scoping',
  'transform-es2015-computed-properties',
  'transform-es2015-destructuring',
  'transform-es2015-function-name',
  'transform-es2015-literals',
  'transform-es2015-parameters',
  'transform-es2015-shorthand-properties',
  'transform-flow-strip-types',
  'transform-react-jsx',
  'transform-regenerator',
  [
    'transform-es2015-modules-commonjs',
    {strict: false, allowTopLevelThis: true},
  ],
]);

const checkES2015Constants = resolvePlugin('check-es2015-constants');
const es2015ArrowFunctions = resolvePlugin('transform-es2015-arrow-functions');
const es2015Classes = resolvePlugin('transform-es2015-classes');
const es2015ForOf = resolvePlugin(['transform-es2015-for-of', {loose: true}]);
const es2015Spread = resolvePlugin('transform-es2015-spread');
const es2015TemplateLiterals = resolvePlugin(
  'transform-es2015-template-literals'
);
const asyncFunctions = resolvePlugin('syntax-async-functions');
const exponentiationOperator = resolvePlugin(
  'transform-exponentiation-operator'
);
const objectAssign = resolvePlugin('transform-object-assign');
const objectRestSpread = resolvePlugin('transform-object-rest-spread');
const reactDisplayName = resolvePlugin('transform-react-display-name');
const reactJsxSource = resolvePlugin('transform-react-jsx-source');
const symbolMember = [require('../transforms/transform-symbol-member')];

const getPreset = (src, options) => {
  const isNull = src === null || src === undefined;
  const hasClass = isNull || src.indexOf('class') !== -1;
  const hasForOf =
    isNull || (src.indexOf('for') !== -1 && src.indexOf('of') !== -1);

  const extraPlugins = [];

  if (isNull || src.indexOf('async') !== -1 || src.indexOf('await') !== -1) {
    extraPlugins.push(asyncFunctions);
  }
  if (hasClass) {
    extraPlugins.push(es2015Classes);
  }
  if (isNull || src.indexOf('=>') !== -1) {
    extraPlugins.push(es2015ArrowFunctions);
  }
  if (isNull || src.indexOf('const') !== -1) {
    extraPlugins.push(checkES2015Constants);
  }
  if (isNull || hasClass || src.indexOf('...') !== -1) {
    extraPlugins.push(es2015Spread);
    extraPlugins.push(objectRestSpread);
  }
  if (isNull || src.indexOf('`') !== -1) {
    extraPlugins.push(es2015TemplateLiterals);
  }
  if (isNull || src.indexOf('**') !== -1) {
    extraPlugins.push(exponentiationOperator);
  }
  if (isNull || src.indexOf('Object.assign') !== -1) {
    extraPlugins.push(objectAssign);
  }
  if (hasForOf) {
    extraPlugins.push(es2015ForOf);
  }
  if (hasForOf || src.indexOf('Symbol') !== -1) {
    extraPlugins.push(symbolMember);
  }
  if (
    isNull ||
    src.indexOf('React.createClass') !== -1 ||
    src.indexOf('createReactClass') !== -1
  ) {
    extraPlugins.push(reactDisplayName);
  }

  if (options && options.dev) {
    extraPlugins.push(reactJsxSource);
  }

  return {
    comments: false,
    compact: true,
    plugins: defaultPlugins.concat(extraPlugins),
  };
};

const base = getPreset(null);
const devTools = getPreset(null, {dev: true});

module.exports = options => {
  if (options.withDevTools == null) {
    const env = process.env.BABEL_ENV || process.env.NODE_ENV;
    if (!env || env === 'development') {
      return devTools;
    }
  }
  return base;
};

module.exports.getPreset = getPreset;
<commit_msg>Refactor resolvePlugins out to prepare for babel 7<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';

const defaultPlugins = [
  [require('babel-plugin-syntax-class-properties')],
  [require('babel-plugin-syntax-trailing-function-commas')],
  [require('babel-plugin-transform-class-properties')],
  [require('babel-plugin-transform-es2015-block-scoping')],
  [require('babel-plugin-transform-es2015-computed-properties')],
  [require('babel-plugin-transform-es2015-destructuring')],
  [require('babel-plugin-transform-es2015-function-name')],
  [require('babel-plugin-transform-es2015-literals')],
  [require('babel-plugin-transform-es2015-parameters')],
  [require('babel-plugin-transform-es2015-shorthand-properties')],
  [require('babel-plugin-transform-flow-strip-types')],
  [require('babel-plugin-transform-react-jsx')],
  [require('babel-plugin-transform-regenerator')],
  [
    require('babel-plugin-transform-es2015-modules-commonjs'),
    {strict: false, allowTopLevelThis: true},
  ],
];

const checkES2015Constants = [require('babel-plugin-check-es2015-constants')];
const es2015ArrowFunctions = [require('babel-plugin-transform-es2015-arrow-functions')];
const es2015Classes = [require('babel-plugin-transform-es2015-classes')];
const es2015ForOf = [require('babel-plugin-transform-es2015-for-of'), {loose: true}];
const es2015Spread = [require('babel-plugin-transform-es2015-spread')];
const es2015TemplateLiterals = [require('babel-plugin-transform-es2015-template-literals')];
const asyncFunctions = [require('babel-plugin-syntax-async-functions')];
const exponentiationOperator = [require('babel-plugin-transform-exponentiation-operator')];
const objectAssign = [require('babel-plugin-transform-object-assign')];
const objectRestSpread = [require('babel-plugin-transform-object-rest-spread')];
const reactDisplayName = [require('babel-plugin-transform-react-display-name')];
const reactJsxSource = [require('babel-plugin-transform-react-jsx-source')];
const symbolMember = [require('../transforms/transform-symbol-member')];

const getPreset = (src, options) => {
  const isNull = src === null || src === undefined;
  const hasClass = isNull || src.indexOf('class') !== -1;
  const hasForOf =
    isNull || (src.indexOf('for') !== -1 && src.indexOf('of') !== -1);

  const extraPlugins = [];

  if (isNull || src.indexOf('async') !== -1 || src.indexOf('await') !== -1) {
    extraPlugins.push(asyncFunctions);
  }
  if (hasClass) {
    extraPlugins.push(es2015Classes);
  }
  if (isNull || src.indexOf('=>') !== -1) {
    extraPlugins.push(es2015ArrowFunctions);
  }
  if (isNull || src.indexOf('const') !== -1) {
    extraPlugins.push(checkES2015Constants);
  }
  if (isNull || hasClass || src.indexOf('...') !== -1) {
    extraPlugins.push(es2015Spread);
    extraPlugins.push(objectRestSpread);
  }
  if (isNull || src.indexOf('`') !== -1) {
    extraPlugins.push(es2015TemplateLiterals);
  }
  if (isNull || src.indexOf('**') !== -1) {
    extraPlugins.push(exponentiationOperator);
  }
  if (isNull || src.indexOf('Object.assign') !== -1) {
    extraPlugins.push(objectAssign);
  }
  if (hasForOf) {
    extraPlugins.push(es2015ForOf);
  }
  if (hasForOf || src.indexOf('Symbol') !== -1) {
    extraPlugins.push(symbolMember);
  }
  if (
    isNull ||
    src.indexOf('React.createClass') !== -1 ||
    src.indexOf('createReactClass') !== -1
  ) {
    extraPlugins.push(reactDisplayName);
  }

  if (options && options.dev) {
    extraPlugins.push(reactJsxSource);
  }

  return {
    comments: false,
    compact: true,
    plugins: defaultPlugins.concat(extraPlugins),
  };
};

const base = getPreset(null);
const devTools = getPreset(null, {dev: true});

module.exports = options => {
  if (options.withDevTools == null) {
    const env = process.env.BABEL_ENV || process.env.NODE_ENV;
    if (!env || env === 'development') {
      return devTools;
    }
  }
  return base;
};

module.exports.getPreset = getPreset;
<|endoftext|>


<commit_before>"use strict"

var Backspace = 8;

// See http://stackoverflow.com/questions/12949590/how-to-detach-event-in-ie-6-7-8-9-using-javascript
function addHandler(element, type, handler) {
    if (element.addEventListener) {
        element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + type, handler);
    } else {
        element["on" + type] = handler;
    }
}
function removeHandler(element, type, handler) {
    if (element.removeEventListener) {
        element.removeEventListener(type, handler, false);
    } else if (element.detachEvent) {
        element.detachEvent("on" + type, handler);
    } else {
        element["on" + type] = null;
    }
}

// Test wether or not the given node is an active contenteditable,
// or is inside an active contenteditable
function isInActiveContentEditable(node) {
    while (node) {
        if ( node.getAttribute && node.getAttribute("contenteditable") === "true" ) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

var ValidInputTypes = ['TEXT','PASSWORD','FILE','EMAIL','SEARCH','DATE'];

function isActiveFormItem(node) {
    var tagName = node.tagName.toUpperCase();
    var isInput = ( tagName === "INPUT" && ValidInputTypes.indexOf(node.type.toUpperCase()) >= 0 );
    var isTextarea = ( tagName === "TEXTAREA" );
    if ( isInput || isTextarea ) {
        var isDisabled = node.readOnly || node.disabled;
        return !isDisabled;
    }
    else if ( isInActiveContentEditable(node) ) {
        return true;
    }
    else {
        return false;
    }
}

// See http://stackoverflow.com/questions/1495219/how-can-i-prevent-the-backspace-key-from-navigating-back
function disabler(event) {
    if (event.keyCode === Backspace) {
        var node = event.srcElement || event.target;
        // We don't want to disable the ability to delete content in form inputs and contenteditables
        if ( isActiveFormItem(node) ) {
            // Do nothing
        }
        // But in any other cases we prevent the default behavior that triggers a browser backward navigation
        else {
            event.preventDefault();
        }
    }
}

/**
 * By default the browser issues a back nav when the focus is not on a form input / textarea
 * But users often press back without focus, and they loose all their form data :(
 *
 * Use this if you want the backspace to never trigger a browser back
 */
exports.disable = function(el) {
    addHandler(el || document,"keydown",disabler);
};

/**
 * Reenable the browser backs
 */
exports.enable = function(el) {
    removeHandler(el || document,"keydown",disabler);
};

<commit_msg>Make check of contenteditable attribute value case insensitive<commit_after>"use strict"

var Backspace = 8;

// See http://stackoverflow.com/questions/12949590/how-to-detach-event-in-ie-6-7-8-9-using-javascript
function addHandler(element, type, handler) {
    if (element.addEventListener) {
        element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + type, handler);
    } else {
        element["on" + type] = handler;
    }
}
function removeHandler(element, type, handler) {
    if (element.removeEventListener) {
        element.removeEventListener(type, handler, false);
    } else if (element.detachEvent) {
        element.detachEvent("on" + type, handler);
    } else {
        element["on" + type] = null;
    }
}

// Test wether or not the given node is an active contenteditable,
// or is inside an active contenteditable
function isInActiveContentEditable(node) {
    while (node) {
        if ( node.getAttribute &&
             node.getAttribute("contenteditable") &&
             node.getAttribute("contenteditable").toUpperCase() === "TRUE" ) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

var ValidInputTypes = ['TEXT','PASSWORD','FILE','EMAIL','SEARCH','DATE'];

function isActiveFormItem(node) {
    var tagName = node.tagName.toUpperCase();
    var isInput = ( tagName === "INPUT" && ValidInputTypes.indexOf(node.type.toUpperCase()) >= 0 );
    var isTextarea = ( tagName === "TEXTAREA" );
    if ( isInput || isTextarea ) {
        var isDisabled = node.readOnly || node.disabled;
        return !isDisabled;
    }
    else if ( isInActiveContentEditable(node) ) {
        return true;
    }
    else {
        return false;
    }
}

// See http://stackoverflow.com/questions/1495219/how-can-i-prevent-the-backspace-key-from-navigating-back
function disabler(event) {
    if (event.keyCode === Backspace) {
        var node = event.srcElement || event.target;
        // We don't want to disable the ability to delete content in form inputs and contenteditables
        if ( isActiveFormItem(node) ) {
            // Do nothing
        }
        // But in any other cases we prevent the default behavior that triggers a browser backward navigation
        else {
            event.preventDefault();
        }
    }
}

/**
 * By default the browser issues a back nav when the focus is not on a form input / textarea
 * But users often press back without focus, and they loose all their form data :(
 *
 * Use this if you want the backspace to never trigger a browser back
 */
exports.disable = function(el) {
    addHandler(el || document,"keydown",disabler);
};

/**
 * Reenable the browser backs
 */
exports.enable = function(el) {
    removeHandler(el || document,"keydown",disabler);
};

<|endoftext|>


<commit_before>/*!
 * jStorage
 * https://github.com/flowertwig-org/jStorage
 *
 * Copyright 2013 Mattias Blomqvist and other contributors
 * Released under the MIT license
 * https://github.com/flowertwig-org/jStorage/blob/master/LICENSE
 *
 * Date: 21:09 2013-12-30
 */
(function (window, undefined) {
    var rootjStorage;
    var jStorage = function (config) {
        // The jStorage object is actually just the init constructor 'enhanced'
        return new jStorage.fn.init(config);
    };
    jStorage.fn = jStorage.prototype = {
        init: function (config) {
            this._provider = false;

            // TODO: do a config and config.name check..

            if (jStorage.providers[config.name]) {
                var provider = jStorage.providers[config.name];
                this._provider = provider;
                provider.init(this, config);
            } else {
                //console.log('Storage provider ' + config.name + ' was not loaded.');
                throw 'Storage provider ' + config.name + ' was not loaded.';
            }
        },
        get: function (name, callback) {
            if (this._provider) {
                this._provider.get(name, callback);
            }
        },
        set: function(name, content, callback) {
            if (this._provider) {
                this._provider.set(name, content, callback);
            }
        },
        del: function (name, callback) {
            if (this._provider) {
                this._provider.del(name, callback);
            }
        },
        list: function (name, callback) {
            if (this._provider) {
                this._provider.list(name, callback);
            }
        },
        exists: function (name, callback) {
            //console.log('exists');
            if (this._provider) {
                this._provider.exists();
            }
        }
    };

    jStorage.providers = jStorage.prototype = {

    };

    // Give the init function the jStorage prototype for later instantiation
    jStorage.fn.init.prototype = jStorage.fn;

    rootjStorage = jStorage;
    window.jStorage = jStorage;

}(window));<commit_msg>Document a not ubiquitous pattern.<commit_after>/*!
 * jStorage
 * https://github.com/flowertwig-org/jStorage
 *
 * Copyright 2013 Mattias Blomqvist and other contributors
 * Released under the MIT license
 * https://github.com/flowertwig-org/jStorage/blob/master/LICENSE
 *
 * Date: 21:09 2013-12-30
 */

// Creating a closure to avoid leaking variables into global scope,
// and using the variable undefined to get a X-browser compatible
// way of comparing with undefined
(function (window, undefined) {
    var rootjStorage;
    var jStorage = function (config) {
        // The jStorage object is actually just the init constructor 'enhanced'
        return new jStorage.fn.init(config);
    };
    jStorage.fn = jStorage.prototype = {
        init: function (config) {
            this._provider = false;

            // TODO: do a config and config.name check..

            if (jStorage.providers[config.name]) {
                var provider = jStorage.providers[config.name];
                this._provider = provider;
                provider.init(this, config);
            } else {
                //console.log('Storage provider ' + config.name + ' was not loaded.');
                throw 'Storage provider ' + config.name + ' was not loaded.';
            }
        },
        get: function (name, callback) {
            if (this._provider) {
                this._provider.get(name, callback);
            }
        },
        set: function(name, content, callback) {
            if (this._provider) {
                this._provider.set(name, content, callback);
            }
        },
        del: function (name, callback) {
            if (this._provider) {
                this._provider.del(name, callback);
            }
        },
        list: function (name, callback) {
            if (this._provider) {
                this._provider.list(name, callback);
            }
        },
        exists: function (name, callback) {
            //console.log('exists');
            if (this._provider) {
                this._provider.exists();
            }
        }
    };

    jStorage.providers = jStorage.prototype = {

    };

    // Give the init function the jStorage prototype for later instantiation
    jStorage.fn.init.prototype = jStorage.fn;

    rootjStorage = jStorage;
    window.jStorage = jStorage;

}(window));<|endoftext|>


<commit_before>var deepcopy = require('deepcopy');
var util = require('util');

var Experiment = module.exports;

/* The organizational strategy followed below is a compound (two-layer) monad.
 * These monadic parts are then used to construct increasingly complex
 * experiment combinators, which are finally used to construct particular
 * experiment running networks.
 *
 * Such networks are driver by the Experiment.run entry point, which couples a
 * given network to a particular result sink (which defaults inspects to stdout).
 *
 * So our monad :: spec -> (emit) -> null
 * where emit   :: spec -> null
 * and spec     :: {...}
 *
 * In other words we have:
 * - functions which take
 * - some arbitrary specification object
 * - and return a continuation
 * - which will receive spec objects
 *
 * So we'll stage it in two layers; some of the parts below, especially the
 * unit functions, are provided solely for elucidation/completeness and aren't
 * used much (if at all).
 */

// TODO: the names really could use some reconsideration

// TODO: we may be able to simplify significantly by using some curry

// The outer layer is the spec -> cont functions:
Experiment.cont = function(cont) { // outer unit
  return function(spec) {return cont;};
};

Experiment.before = function(exp, f) { // outer bind
  return function(spec) {
    return f(exp(spec));
  };
};

Experiment.wrap = function(f) {
  return function(exp) {
    return function(spec) {
      return f(exp, spec);
    };
  };
};

Experiment.setup = function(f) {
  return Experiment.wrap(function(exp, spec) {
    return exp(f(spec) || spec);
  });
};

// The inner layer is the continuations themselves:

Experiment.under = function(f) {
  return function(spec) {
    return function(emit) {
      f(spec, emit);
    };
  };
};

Experiment.returnResult = function(result) { // inner unit
  return Experimnt.cont(function(emit) {emit(result);});
};

Experiment.after = function(f) { // inner bind
  return Experiment.wrap(function(exp, spec) {
    var cont = exp(deepcopy(spec));
    return function(emit) {
      cont(function(result) {emit(f(result) || result);});
    };
  });
};

Experiment.compute = function(prop, f) {
  if (arguments.length === 1) {
    f = prop;
    if (! f.name) throw new Error('specify property name, or name your function');
    prop = f.name;
  }
  var argNames = /\((.+?)\)/.exec(f.toString())[1].split(/,\s*/g);
  return Experiment.after(function(result) {
    var args = [];
    for (var i=0, n=argNames.length; i<n; i++) {
      var argName = argNames[i];
      if (result[argName] === undefined) return;
      args.push(result[argName]);
    }
    result[prop] = f.apply(this, args);
  });
};

Experiment.transform = function(prop, f) {
  return Experiment.after(function(result) {
    if (result[prop] !== undefined)
      result[prop] = f(result[prop]);
  });
};

// Now for generic parts which operate at both layers:

Experiment.run = function(exp, spec, emit) {
  if (! exp) throw new Error('no experiment given to run');
  if (typeof spec == 'function' && arguments.length == 2) {
    emit = spec;
    spec = null;
  }
  if (! spec) spec = {};
  if (spec.emit) {
    if (! emit) emit = spec.emit;
    delete spec.emit;
  }
  emit = emit || Experiment.emitInspectRecords(process.stdout);
  exp(spec)(emit);
};

Experiment.bind = function(exp, f) { // combined bind
  return function(spec) {
    return function(emit) {
      f(exp(spec), emit);
    };
  };
};

Experiment.seq = function(f, g) {
  if (arguments.length > 2)
    g = Experiment.seq.apply(null, Array.prototype.slice.call(arguments, 1));
  return f(g);
};

// And some applications

Experiment.expand = function(exp, spec, expand) {
  if (spec.parallelExpand) {
    return function(emit) {
      while (true) {
        var newspec = deepcopy(spec);
        if (! expand(newspec)) break;
        exp(newspec)(emit);
      }
    };
  } else {
    return function(emit) {
      var it = function() {
        var newspec = deepcopy(spec);
        if (! expand(newspec)) return;
        exp(newspec)(function(result) {
          emit(result);
          setImmediate(it);
        });
      };
      it();
    };
  }
};

Experiment.copy = function(src, dst) {
  return Experiment.setup(function(spec) {
    spec[dst] = deepcopy(spec[src]);
  });
};

Experiment.choose = function(prop, lo, hi) {
  if (hi === undefined) {
    hi = lo;
    lo = 0;
  }
  var width = hi - lo;
  return Experiment.setup(function(spec) {
    spec[prop] = lo + Math.random() * width;
  });
};

Experiment.timed = Experiment.wrap(function(exp, spec) {
  var cont = exp(spec);
  return function(emit) {
    var t0 = process.hrtime();
    cont(function(result) {
      var t1 = process.hrtime();
      result.runtime = (t1[0] - t0[0]) * 1e9 + (t1[1] - t0[1]);
      emit(result);
      t0 = process.hrtime();
    });
  };
});

// TODO: provide an rusage monitor similar to timed

Experiment.times = function(k, prop) {
  if (arguments.length == 1) {
    prop = 'time';
  }
  return Experiment.wrap(function(exp, spec) {
    var i = 0;
    return Experiment.expand(exp, spec, function(spec) {
      if (i < k) {
        spec[prop] = i++;
        return true;
      }
    });
  });
};

Experiment.each = function(plural, singular, spec) {
  return Experiment.wrap(function(exp, spec) {
    var items;
    if (typeof(plural) === 'string') {
      items = spec[plural];
      delete spec[plural];
    } else {
      items = plural;
    }
    var i = 0;
    if (Array.isArray(items)) {
      return Experiment.expand(exp, spec, function(spec) {
        if (i < items.length) {
          spec[singular] = items[i++];
          return true;
        }
      });
    } else {
      items = Object
        .keys(items)
        .sort()
        .map(function(name) {return [name, items[name]];});
      return Experiment.expand(exp, spec, function(spec) {
        if (i < items.length) {
          var item = items[i++];
          spec[singular + '_name'] = item[0];
          spec[singular          ] = item[1];
          return true;
        }
      });
    }
  });
};

Experiment.gen = function(prop, gen, spec) {
  return Experiment.wrap(function(exp, spec) {
    var it = gen(spec, prop);
    return Experiment.expand(exp, spec, function(spec) {
      var val = it();
      if (val !== null && val !== undefined) {
        spec[prop] = val;
        return true;
      }
    });
  });
};

/* Stabilize a particular experiment metric.
 *
 * Takes an options argument; if a single string argument is given, it is
 * created to {prop: <arg>}.
 *
 * Options:
 * - prop      -- name of the property to stabilize
 * - tolerance -- maximum deviation tolerance to achieve
 *                default: 3
 * - count     -- desired sample size within tolerance range
 *                default: 10
 * - augment   -- if truthy, then each emitted result will have a <prop>_desc
 *                attribute added with structure:
 *                {
 *                  sample: [Number], // all result collected to construct the sample
 *                  med:    Number,   // the sample median
 *                  dev:    Number,   // the sample deviation (IQR / 2)
 *                  lo:     Number,   // the low cutoff:  med - tol * dev
 *                  hi:     Number    // the high cutoff: med + tol * dev
 *                }
 *
 * The deviation metric used is IQR/2, or more verbosely:
 *
 *   (sample.quantile(.75) - sample.quantile(.25)) / 2
 *
 * The goal then is to obtain count results within tol*dev of the sample median
 * (a 6-deviation spread centered within the sample space).
 */
Experiment.stabalize = function(options) {
  if (typeof options === 'string') {
    options = {prop: options};
  } else if (! options) {
    options = {};
  }

  if (! options.prop) {
    throw new Error('no property given to stabalize');
  }

  var prop      = options.prop;
  var tolerance = options.tolerance || 3;
  var count     = options.count     || 10;
  var augment   = !!options.augment;

  return Experiment.wrap(function(exp, spec) {
    return function(emit) {
      var time = 0;
      var sample = [];
      var ile = function(p) {
        var n = sample.length;
        var q = p * n;
        var i = Math.min(n-1, Math.floor(q));
        var j = Math.min(n-1, Math.ceil(q));
        return (sample[i][prop] + sample[j][prop]) / 2;
      };

      var it = function() {
        var run = deepcopy(spec);
        run.time = ++time;
        exp(run)(function(result) {
          sample.push(result);
          sample.sort(function(a, b) {
            return a[prop] - b[prop];
          });

          if (sample.length >= count) {
            var desc = {sample: sample};
            desc.med = ile(0.50);
            desc.dev = (ile(0.75) - ile(0.25)) / 2;
            desc.hi  = desc.med + tolerance * desc.dev;
            desc.lo  = Math.max(0, desc.med - tolerance * desc.dev);

            var i=null, j=null;
            sample.forEach(function(result, k) {
              if (desc.lo <= result[prop] && result[prop] <= desc.hi) {
                if (i === null) i = k;
                j = k;
              }
            });

            var got = 1 + j - i;
            if (got >= count) {
              j = j - (got - count);
              for (; i<=j; i++) {
                if (augment) {
                  sample[i][prop + '_desc'] = desc;
                }
                emit(sample[i]);
              }
              return;
            }
          }

          it();
        });
      };
      it();
    };
  });
};

// -- emission utilities (sinks)

Experiment.emitSepRecords = function(stream, sep) {
  sep = sep || ' ';
  var fields = null;
  return function(spec) {
    if (! fields ) {
      fields = Object
        .keys(spec)
        .filter(function(prop) {
          return typeof spec[prop] !== 'function';
        })
        .sort();
      stream.write(fields.join(' ') + '\n');
    }
    stream.write(
      fields
        .map(function(field) {return spec[field];})
        .join(' ') + '\n'
    );
  };
};

Experiment.emitInspectRecords = function(stream) {
  return function(result) {
    stream.write(util.inspect(result, {depth: null}));
  };
};

// -- interactive / test harness

if (require.main === module) {

  // Measures the difference between desired time to sleep and time slept by
  // picking a 3 random values < 100ms and sleeping for each value 10 times
  var sleepExp =
    Experiment.seq(
      Experiment.times(3),
      Experiment.choose('delay', 100),
      Experiment.times(10),

      // NOTE: not clear here, but Experiment.after executes in REVERSE order
      // under seq since it happens as the stack unwinds on the way out
      // TODO: maybe provide an inner chaining/seq combinator to hide this detail?
      Experiment.compute(function diff(delay, runtime) {
        return 1 - delay / (runtime / 1e6);
      }),

      Experiment.timed(
        Experiment.under(function(spec, emit) {
          setTimeout(function() {
            emit(spec);
          }, spec.delay);
        })
      )

    )
    ;

  /* TODO: write a parsing layer which transforms the following into the above:
   *
   * [
   *   {$times: 3},
   *   {$choose: ['delay', 100]},
   *   {$times: 10},
   *   {$compute: function diff(delay, runtime) {
   *                return 1 - delay / (runtime / 1e6);
   *              }},
   *   {$timed: {$under: function(spec, emit) {
   *     setTimeout(function() {
   *       emit(spec);
   *     }, spec.delay);
   *   }}}
   * ]
   */

  // TODO: write something more advanced like Experiment.cliHarness which
  // allows to specify arbitrary spec properties
  Experiment.run(
    sleepExp,
    Experiment.emitSepRecords(process.stdout)
  );
}

<commit_msg>Tweak times signature<commit_after>var deepcopy = require('deepcopy');
var util = require('util');

var Experiment = module.exports;

/* The organizational strategy followed below is a compound (two-layer) monad.
 * These monadic parts are then used to construct increasingly complex
 * experiment combinators, which are finally used to construct particular
 * experiment running networks.
 *
 * Such networks are driver by the Experiment.run entry point, which couples a
 * given network to a particular result sink (which defaults inspects to stdout).
 *
 * So our monad :: spec -> (emit) -> null
 * where emit   :: spec -> null
 * and spec     :: {...}
 *
 * In other words we have:
 * - functions which take
 * - some arbitrary specification object
 * - and return a continuation
 * - which will receive spec objects
 *
 * So we'll stage it in two layers; some of the parts below, especially the
 * unit functions, are provided solely for elucidation/completeness and aren't
 * used much (if at all).
 */

// TODO: the names really could use some reconsideration

// TODO: we may be able to simplify significantly by using some curry

// The outer layer is the spec -> cont functions:
Experiment.cont = function(cont) { // outer unit
  return function(spec) {return cont;};
};

Experiment.before = function(exp, f) { // outer bind
  return function(spec) {
    return f(exp(spec));
  };
};

Experiment.wrap = function(f) {
  return function(exp) {
    return function(spec) {
      return f(exp, spec);
    };
  };
};

Experiment.setup = function(f) {
  return Experiment.wrap(function(exp, spec) {
    return exp(f(spec) || spec);
  });
};

// The inner layer is the continuations themselves:

Experiment.under = function(f) {
  return function(spec) {
    return function(emit) {
      f(spec, emit);
    };
  };
};

Experiment.returnResult = function(result) { // inner unit
  return Experimnt.cont(function(emit) {emit(result);});
};

Experiment.after = function(f) { // inner bind
  return Experiment.wrap(function(exp, spec) {
    var cont = exp(deepcopy(spec));
    return function(emit) {
      cont(function(result) {emit(f(result) || result);});
    };
  });
};

Experiment.compute = function(prop, f) {
  if (arguments.length === 1) {
    f = prop;
    if (! f.name) throw new Error('specify property name, or name your function');
    prop = f.name;
  }
  var argNames = /\((.+?)\)/.exec(f.toString())[1].split(/,\s*/g);
  return Experiment.after(function(result) {
    var args = [];
    for (var i=0, n=argNames.length; i<n; i++) {
      var argName = argNames[i];
      if (result[argName] === undefined) return;
      args.push(result[argName]);
    }
    result[prop] = f.apply(this, args);
  });
};

Experiment.transform = function(prop, f) {
  return Experiment.after(function(result) {
    if (result[prop] !== undefined)
      result[prop] = f(result[prop]);
  });
};

// Now for generic parts which operate at both layers:

Experiment.run = function(exp, spec, emit) {
  if (! exp) throw new Error('no experiment given to run');
  if (typeof spec == 'function' && arguments.length == 2) {
    emit = spec;
    spec = null;
  }
  if (! spec) spec = {};
  if (spec.emit) {
    if (! emit) emit = spec.emit;
    delete spec.emit;
  }
  emit = emit || Experiment.emitInspectRecords(process.stdout);
  exp(spec)(emit);
};

Experiment.bind = function(exp, f) { // combined bind
  return function(spec) {
    return function(emit) {
      f(exp(spec), emit);
    };
  };
};

Experiment.seq = function(f, g) {
  if (arguments.length > 2)
    g = Experiment.seq.apply(null, Array.prototype.slice.call(arguments, 1));
  return f(g);
};

// And some applications

Experiment.expand = function(exp, spec, expand) {
  if (spec.parallelExpand) {
    return function(emit) {
      while (true) {
        var newspec = deepcopy(spec);
        if (! expand(newspec)) break;
        exp(newspec)(emit);
      }
    };
  } else {
    return function(emit) {
      var it = function() {
        var newspec = deepcopy(spec);
        if (! expand(newspec)) return;
        exp(newspec)(function(result) {
          emit(result);
          setImmediate(it);
        });
      };
      it();
    };
  }
};

Experiment.copy = function(src, dst) {
  return Experiment.setup(function(spec) {
    spec[dst] = deepcopy(spec[src]);
  });
};

Experiment.choose = function(prop, lo, hi) {
  if (hi === undefined) {
    hi = lo;
    lo = 0;
  }
  var width = hi - lo;
  return Experiment.setup(function(spec) {
    spec[prop] = lo + Math.random() * width;
  });
};

Experiment.timed = Experiment.wrap(function(exp, spec) {
  var cont = exp(spec);
  return function(emit) {
    var t0 = process.hrtime();
    cont(function(result) {
      var t1 = process.hrtime();
      result.runtime = (t1[0] - t0[0]) * 1e9 + (t1[1] - t0[1]);
      emit(result);
      t0 = process.hrtime();
    });
  };
});

// TODO: provide an rusage monitor similar to timed

Experiment.times = function(prop, k) {
  if (arguments.length == 1) {
    k = prop;
    prop = 'time';
  }
  return Experiment.wrap(function(exp, spec) {
    var i = 0;
    return Experiment.expand(exp, spec, function(spec) {
      if (i < k) {
        spec[prop] = i++;
        return true;
      }
    });
  });
};

Experiment.each = function(plural, singular, spec) {
  return Experiment.wrap(function(exp, spec) {
    var items;
    if (typeof(plural) === 'string') {
      items = spec[plural];
      delete spec[plural];
    } else {
      items = plural;
    }
    var i = 0;
    if (Array.isArray(items)) {
      return Experiment.expand(exp, spec, function(spec) {
        if (i < items.length) {
          spec[singular] = items[i++];
          return true;
        }
      });
    } else {
      items = Object
        .keys(items)
        .sort()
        .map(function(name) {return [name, items[name]];});
      return Experiment.expand(exp, spec, function(spec) {
        if (i < items.length) {
          var item = items[i++];
          spec[singular + '_name'] = item[0];
          spec[singular          ] = item[1];
          return true;
        }
      });
    }
  });
};

Experiment.gen = function(prop, gen, spec) {
  return Experiment.wrap(function(exp, spec) {
    var it = gen(spec, prop);
    return Experiment.expand(exp, spec, function(spec) {
      var val = it();
      if (val !== null && val !== undefined) {
        spec[prop] = val;
        return true;
      }
    });
  });
};

/* Stabilize a particular experiment metric.
 *
 * Takes an options argument; if a single string argument is given, it is
 * created to {prop: <arg>}.
 *
 * Options:
 * - prop      -- name of the property to stabilize
 * - tolerance -- maximum deviation tolerance to achieve
 *                default: 3
 * - count     -- desired sample size within tolerance range
 *                default: 10
 * - augment   -- if truthy, then each emitted result will have a <prop>_desc
 *                attribute added with structure:
 *                {
 *                  sample: [Number], // all result collected to construct the sample
 *                  med:    Number,   // the sample median
 *                  dev:    Number,   // the sample deviation (IQR / 2)
 *                  lo:     Number,   // the low cutoff:  med - tol * dev
 *                  hi:     Number    // the high cutoff: med + tol * dev
 *                }
 *
 * The deviation metric used is IQR/2, or more verbosely:
 *
 *   (sample.quantile(.75) - sample.quantile(.25)) / 2
 *
 * The goal then is to obtain count results within tol*dev of the sample median
 * (a 6-deviation spread centered within the sample space).
 */
Experiment.stabalize = function(options) {
  if (typeof options === 'string') {
    options = {prop: options};
  } else if (! options) {
    options = {};
  }

  if (! options.prop) {
    throw new Error('no property given to stabalize');
  }

  var prop      = options.prop;
  var tolerance = options.tolerance || 3;
  var count     = options.count     || 10;
  var augment   = !!options.augment;

  return Experiment.wrap(function(exp, spec) {
    return function(emit) {
      var time = 0;
      var sample = [];
      var ile = function(p) {
        var n = sample.length;
        var q = p * n;
        var i = Math.min(n-1, Math.floor(q));
        var j = Math.min(n-1, Math.ceil(q));
        return (sample[i][prop] + sample[j][prop]) / 2;
      };

      var it = function() {
        var run = deepcopy(spec);
        run.time = ++time;
        exp(run)(function(result) {
          sample.push(result);
          sample.sort(function(a, b) {
            return a[prop] - b[prop];
          });

          if (sample.length >= count) {
            var desc = {sample: sample};
            desc.med = ile(0.50);
            desc.dev = (ile(0.75) - ile(0.25)) / 2;
            desc.hi  = desc.med + tolerance * desc.dev;
            desc.lo  = Math.max(0, desc.med - tolerance * desc.dev);

            var i=null, j=null;
            sample.forEach(function(result, k) {
              if (desc.lo <= result[prop] && result[prop] <= desc.hi) {
                if (i === null) i = k;
                j = k;
              }
            });

            var got = 1 + j - i;
            if (got >= count) {
              j = j - (got - count);
              for (; i<=j; i++) {
                if (augment) {
                  sample[i][prop + '_desc'] = desc;
                }
                emit(sample[i]);
              }
              return;
            }
          }

          it();
        });
      };
      it();
    };
  });
};

// -- emission utilities (sinks)

Experiment.emitSepRecords = function(stream, sep) {
  sep = sep || ' ';
  var fields = null;
  return function(spec) {
    if (! fields ) {
      fields = Object
        .keys(spec)
        .filter(function(prop) {
          return typeof spec[prop] !== 'function';
        })
        .sort();
      stream.write(fields.join(' ') + '\n');
    }
    stream.write(
      fields
        .map(function(field) {return spec[field];})
        .join(' ') + '\n'
    );
  };
};

Experiment.emitInspectRecords = function(stream) {
  return function(result) {
    stream.write(util.inspect(result, {depth: null}));
  };
};

// -- interactive / test harness

if (require.main === module) {

  // Measures the difference between desired time to sleep and time slept by
  // picking a 3 random values < 100ms and sleeping for each value 10 times
  var sleepExp =
    Experiment.seq(
      Experiment.times(3),
      Experiment.choose('delay', 100),
      Experiment.times(10),

      // NOTE: not clear here, but Experiment.after executes in REVERSE order
      // under seq since it happens as the stack unwinds on the way out
      // TODO: maybe provide an inner chaining/seq combinator to hide this detail?
      Experiment.compute(function diff(delay, runtime) {
        return 1 - delay / (runtime / 1e6);
      }),

      Experiment.timed(
        Experiment.under(function(spec, emit) {
          setTimeout(function() {
            emit(spec);
          }, spec.delay);
        })
      )

    )
    ;

  /* TODO: write a parsing layer which transforms the following into the above:
   *
   * [
   *   {$times: 3},
   *   {$choose: ['delay', 100]},
   *   {$times: 10},
   *   {$compute: function diff(delay, runtime) {
   *                return 1 - delay / (runtime / 1e6);
   *              }},
   *   {$timed: {$under: function(spec, emit) {
   *     setTimeout(function() {
   *       emit(spec);
   *     }, spec.delay);
   *   }}}
   * ]
   */

  // TODO: write something more advanced like Experiment.cliHarness which
  // allows to specify arbitrary spec properties
  Experiment.run(
    sleepExp,
    Experiment.emitSepRecords(process.stdout)
  );
}

<|endoftext|>


<commit_before>window.addEvent('domready', function() {

	// Fluid 16-column Grid
	// Auto-Select Toggle Elements

	// Select all boxes with h2 anchor elements and toggle the next div
	// The toggle-ajax anchor is excluded or the AJAX content box won't work
	$$('.box h2 a[id!=toggle-ajax]').each(function(item) {
		item.setStyle('cursor', 'pointer');
		var elToHide = item.getParent('h2').getNext('div');
		if (elToHide) {
			var mySlide = new Fx.Slide(elToHide);
			item.store('fx', mySlide);

			item.addEvent('click', function(e) {
				e.stop();
				var fx = this.retrieve('fx');
				fx.toggle();

				if (item.hasClass('hidden')){
					item.removeClass('hidden').addClass('visible');
				}
				else {
					item.removeClass('visible').addClass('hidden');
				}
			});
		}
	});

	// Kwick Box
	if ($('kwick-box')) {
		var kwicks = $$('#kwick .kwick');
		var fx = new Fx.Elements(kwicks, {wait: false, duration: 200, transition: Fx.Transitions.quadOut});
		kwicks.each(function(kwick, i){
			kwick.addEvent('mouseenter', function(e){
				var obj = {};
				obj[i] = {
					'width': [kwick.getStyle('width').toInt(), 485]
				};
				kwicks.each(function(other, j){
					if (other != kwick){
						var w = other.getStyle('width').toInt();
						if (w != 125) obj[j] = {'width': [w, 125]};
					}
				});
				fx.start(obj);
			});
		});

		$('kwick').addEvent('mouseleave', function(e){
			var obj = {};
			kwicks.each(function(other, j){
				obj[j] = {'width': [other.getStyle('width').toInt(), 215]};
			});
			fx.start(obj);
		});
	}

	// Accordion Section Menu
	if ($('section-menu')) {
		var accordion = new Accordion('a.menuitem', 'ul.submenu', {
			opacity: false,
			onActive: function(toggler, element){
				toggler.addClass('current');
				element.addClass('current');
			},
			onBackground: function(toggler, element){
				toggler.removeClass('current');
				element.removeClass('current');
			}
		}, $('section-menu'));
	}

	// Accordion Content
	if ($('accordion')) {
		var accordion = new Accordion('h3.atStart', 'div.atStart', {
			opacity: false,
			onActive: function(toggler, element){
				toggler.setStyle('font-weight', 'bold');
				toggler.setStyle('background', '#fff');
			},

			onBackground: function(toggler, element){
				toggler.setStyle('font-weight', 'normal');
				toggler.setStyle('background', '#eee');
			}
		}, $('accordion'));
	}

	// Toggle and load AJAX content
	if ($('toggle-ajax')) {
		// Dynamically loaded AJAX content
		$('loading').setStyle('display', 'none'); // hides the loading image
		var content = new Fx.Slide('ajax-content').hide(); // creates new Fx.Slide object from ajax-content div, also the hide() function hides the div when the page loads
		$('toggle-ajax').addEvent('click', function(e) { // adds an onClick event to toggle-ajax div
			e = new Event(e);
			if ($('toggle-ajax').hasClass('hidden')) { // checks if the content is visible
				$('ajax-content').empty() // empties the ajax-content
				$('loading').setStyle('display', 'block'); // displays the loading image

				var req = new Request.HTML({
					method: 'get',
					url: '/sym/fluid960gs/workspace/public/data/ajax-response.html',
					update: $('ajax-content'),
					onComplete: function(html) {
						$('loading').setStyle('display', 'none'); //Hides the loading image
						content.toggle(); //toggles the ajax-content
					}
				}).send();
			}
			else {
				content.toggle(); //in case we dont want to send ajax request, we just slide it out
			}
			if ($('toggle-ajax').hasClass('hidden')){
				$('toggle-ajax').removeClass('hidden').addClass('visible');
			}
			else {
				$('toggle-ajax').removeClass('visible').addClass('hidden');
			}
			e.stop(); //this makes sure that the user wont be sent to given url (or that the page refreshes when using dummy url like "#") if the clicked element was a link
		});
	}

	// Toggle Grid
	if ($('toggle-grid')) {
		var grid = new Fx.Slide('grid').hide(); //creates new Fx.Slide object from grid div
		$('toggle-grid').addEvent('click', function(e) { //Adds an onClick event to toggle-grid div
			e = new Event(e);
				grid.toggle(); //toggles the div
			if ($('toggle-grid').hasClass('hidden')){
				$('toggle-grid').removeClass('hidden').addClass('visible');
			}
			else {
				$('toggle-grid').removeClass('visible').addClass('hidden');
			}
			e.stop(); //this makes sure that the user wont be sent to given url (or that the page refreshes when using dummy url like "#") if the clicked element was a link
		});
	}
});<commit_msg>Mootools JavaScript automatically hides div.block when h2 a[class=hide]<commit_after>window.addEvent('domready', function() {

	// Fluid 16-column Grid
	// Auto-Select Toggle Elements

	// Select all boxes with h2 anchor elements and toggle the next div
	// The toggle-ajax anchor is excluded or the AJAX content box won't work
	$$('.box h2 a[id!=toggle-ajax]').each(function(item) {
		item.setStyle('cursor', 'pointer');
		var elToHide = item.getParent('h2').getNext('div');
		if (elToHide) {
			var mySlide = new Fx.Slide(elToHide);
			item.store('fx', mySlide);

			if (item.hasClass('hidden')){
				mySlide.hide();
			}

			item.addEvent('click', function(e) {
				e.stop();
				var fx = this.retrieve('fx');
				fx.toggle();

				if (item.hasClass('hidden')){
					item.removeClass('hidden').addClass('visible');
				}
				else {
					item.removeClass('visible').addClass('hidden');
				}
			});
		}
	});

	// Kwick Box
	if ($('kwick-box')) {
		var kwicks = $$('#kwick .kwick');
		var fx = new Fx.Elements(kwicks, {wait: false, duration: 200, transition: Fx.Transitions.quadOut});
		kwicks.each(function(kwick, i){
			kwick.addEvent('mouseenter', function(e){
				var obj = {};
				obj[i] = {
					'width': [kwick.getStyle('width').toInt(), 485]
				};
				kwicks.each(function(other, j){
					if (other != kwick){
						var w = other.getStyle('width').toInt();
						if (w != 125) obj[j] = {'width': [w, 125]};
					}
				});
				fx.start(obj);
			});
		});

		$('kwick').addEvent('mouseleave', function(e){
			var obj = {};
			kwicks.each(function(other, j){
				obj[j] = {'width': [other.getStyle('width').toInt(), 215]};
			});
			fx.start(obj);
		});
	}

	// Accordion Section Menu
	if ($('section-menu')) {
		var accordion = new Accordion('a.menuitem', 'ul.submenu', {
			opacity: false,
			onActive: function(toggler, element){
				toggler.addClass('current');
				element.addClass('current');
			},
			onBackground: function(toggler, element){
				toggler.removeClass('current');
				element.removeClass('current');
			}
		}, $('section-menu'));
	}

	// Accordion Content
	if ($('accordion')) {
		var accordion = new Accordion('h3.atStart', 'div.atStart', {
			opacity: false,
			onActive: function(toggler, element){
				toggler.setStyle('font-weight', 'bold');
				toggler.setStyle('background', '#fff');
			},

			onBackground: function(toggler, element){
				toggler.setStyle('font-weight', 'normal');
				toggler.setStyle('background', '#eee');
			}
		}, $('accordion'));
	}

	// Toggle and load AJAX content
	if ($('toggle-ajax')) {
		// Dynamically loaded AJAX content
		$('loading').setStyle('display', 'none'); // hides the loading image
		var content = new Fx.Slide('ajax-content').hide(); // creates new Fx.Slide object from ajax-content div, also the hide() function hides the div when the page loads
		$('toggle-ajax').addEvent('click', function(e) { // adds an onClick event to toggle-ajax div
			e = new Event(e);
			if ($('toggle-ajax').hasClass('hidden')) { // checks if the content is visible
				$('ajax-content').empty() // empties the ajax-content
				$('loading').setStyle('display', 'block'); // displays the loading image

				var req = new Request.HTML({
					method: 'get',
					url: '/fluid960gs/data/ajax-response.html',
					update: $('ajax-content'),
					onComplete: function(html) {
						$('loading').setStyle('display', 'none'); //Hides the loading image
						content.toggle(); //toggles the ajax-content
					}
				}).send();
			}
			else {
				content.toggle(); //in case we dont want to send ajax request, we just slide it out
			}
			if ($('toggle-ajax').hasClass('hidden')){
				$('toggle-ajax').removeClass('hidden').addClass('visible');
			}
			else {
				$('toggle-ajax').removeClass('visible').addClass('hidden');
			}
			e.stop(); //this makes sure that the user wont be sent to given url (or that the page refreshes when using dummy url like "#") if the clicked element was a link
		});
	}

	// Toggle Grid
	if ($('toggle-grid')) {
		var grid = new Fx.Slide('grid').hide(); //creates new Fx.Slide object from grid div
		$('toggle-grid').addEvent('click', function(e) { //Adds an onClick event to toggle-grid div
			e = new Event(e);
				grid.toggle(); //toggles the div
			if ($('toggle-grid').hasClass('hidden')){
				$('toggle-grid').removeClass('hidden').addClass('visible');
			}
			else {
				$('toggle-grid').removeClass('visible').addClass('hidden');
			}
			e.stop(); //this makes sure that the user wont be sent to given url (or that the page refreshes when using dummy url like "#") if the clicked element was a link
		});
	}
});<|endoftext|>