<commit_before>/*globals jsbin, _, $, RSVP*/
var processors = jsbin.processors = (function () {
  'use strict';
  /*
   * Debugging note: to emulate a slow connection, or a processor taking too
   * long to load, find the processor in question, and change the `init` method
   * to setTimeout(getScript, n seconds) - this will give you an idea of how
   * jsbin behaves when the processor isn't ready and the user makes calls to it
   */

  /**
   * Add properties to a function using underscore
   */
  var extendFn = function (fn, obj) {
    return _.extend(fn, obj);
  };

  var passthrough = function (ready) { return ready(); };
  var defaultProcessor = function (source) {
    return new RSVP.Promise(function (resolve) {
      resolve(source);
    });
  };

  /**
   * Cache extension ids by their file extensions
   */
  var processorBy = {
    extension: {}
  };

  /**
   * Create a processor – accepts an object containing:
   *
   *    id          Processor name. Required.
   *    target      The target panel. Optional - defaults to the id.
   *    extensions  Possible file extensions for this processor (for gist i/o).
   *                Optional. Defaults to the id.
   *    url         URL of the loader script file. Optional.
   *    init        Setup the processor here. Optional – defaults to the
   *                passthrough (above).
   *    handler     Where the magic happens. Do all processing in here.
   *                Optional - defaults to the defaultProcessor (above).
   */
  var createProcessor = function (opts) {
    var url = opts.url,
        init = opts.init || passthrough,
        handler = opts.handler || defaultProcessor,
        processorData = _.pick(opts, 'id', 'target', 'extensions');

    opts.extensions = opts.extensions || [];
    if (!opts.extensions.length) opts.extensions = [opts.id];

    opts.extensions.forEach(function (ext) {
      processorBy.extension[ext] = opts.id;
    });

    // This actually loads in the processor – script files & init code
    var loadProcessor = function (ready) {

      var failed = false;

      // Overwritten when the script loads
      var callback = function () {
        window.console && window.console.warn('Processor is not ready yet - trying again');
        failed = true;
        return '';
      };

      // Script has loaded.
      // Run any init code, and swap the callback. If we failed, try again.
      var scriptCB = function () {
        init(function () {
          callback = handler;
          if (failed) {
            renderLivePreview();
          }
          ready();
        });
      };

      if (url) {
        // Load the processor's script
        $.getScript(url, scriptCB);
      } else {
        // No url, go straight on
        init(function () {
          callback = handler;
          ready();
        });
      }

      // Create a proxy function that holds the handler in scope so that, when
      // the callbacks are swapped, rendering still works.
      var cache = {
        source: '',
        result: ''
      };
      var proxyCallback = function (source) {
        return new RSVP.Promise(function (resolve, reject) {
          source = source.trim();
          if (source === cache.source) {
            resolve(cache.result);
          } else {
            callback(source, function (result) {
              cache.source = source;
              cache.result = result;
              resolve(result);
            }, reject);
          }
        });
      };

      // Return the method that will be used to render
      return extendFn(proxyCallback, processorData);
    };

    // Processor fucntion also has the important data on it
    return extendFn(loadProcessor, processorData);
  };

  /**
   * JS Bin's processors
   */
  var processors = {

    html: createProcessor({
      id: 'html'
    }),

    css: createProcessor({
      id: 'css'
    }),

    javascript: createProcessor({
      id: 'javascript',
      extensions: ['js']
    }),

    coffeescript: createProcessor({
      id: 'coffeescript',
      target: 'javascript',
      extensions: ['coffee'],
      url: jsbin.static + '/js/vendor/coffee-script.js',
      init: function (ready) {
        $.getScript(jsbin.static + '/js/vendor/codemirror4/mode/coffeescript/coffeescript.js', ready);
      },
      handler: function (source, resolve, reject) {
        var renderedCode = '';
        try {
          renderedCode = CoffeeScript.compile(source, {
            bare: true
          });
          resolve(renderedCode);
        } catch (e) {
          reject(e);
        }
      }
    }),

    jsx: createProcessor({
      id: 'jsx',
      target: 'javascript',
      extensions: ['jsx'],
      url: jsbin.static + '/js/vendor/JSXTransformer.js',
      init: function (ready) {
        // Don't add React if the code already contains a script whose name
        // starts with 'react', to avoid duplicate copies.
        var code = editors.html.getCode();
        if (!(/<script[^>]*src=\S*\breact\b/i).test(code)) {
          $('#library').val( $('#library').find(':contains("React with Add-Ons")').val() ).trigger('change');
        }
        ready();
      },
      handler: function (source, resolve, reject) {
        var renderedCode = '';
        try {
          renderedCode = JSXTransformer.transform(source).code;
          resolve(renderedCode);
        } catch (e) {
          reject(e);
        }
      }
    }),

    typescript: createProcessor({
      id: 'typescript',
      target: 'javascript',
      extensions: ['ts'],
      url: jsbin.static + '/js/vendor/typescript.min.js',
      init: passthrough,
      handler: function (source, resolve, reject) {
        var noop = function () {};
        var outfile = {
          source: '',
          Write: function (s) {
            this.source += s;
          },
          WriteLine: function (s) {
            this.source += s + '\n';
          },
          Close: noop
        };

        var outerr = {
          Write: noop,
          WriteLine: noop,
          Close: noop
        };

        var parseErrors = [];

        var compiler = new TypeScript.TypeScriptCompiler(outfile, outerr);

        compiler.setErrorCallback(function (start, len, message) {
          parseErrors.push({ start: start, len: len, message: message });
        });
        compiler.parser.errorRecovery = true;

        compiler.addUnit(source, 'jsbin.ts');
        compiler.typeCheck();
        compiler.reTypeCheck();
        compiler.emit();

        for (var i = 0, len = parseErrors.length; i < len; i++) {
          console.log('Error Message: ' + parseErrors[i].message);
          console.log('Error Start: ' + parseErrors[i].start);
          console.log('Error Length: ' + parseErrors[i].len);
        }

        if (parseErrors.length) {
          reject();
        } else {
          resolve(outfile.source);
        }
      }
    }),

    markdown: createProcessor({
      id: 'markdown',
      target: 'html',
      extensions: ['md', 'markdown', 'mdown'],
      url: jsbin.static + '/js/vendor/markdown.js',
      init: function (ready) {
        $.getScript(jsbin.static + '/js/vendor/codemirror4/mode/markdown/markdown.js', ready);
      },
      handler: function (source, resolve, reject) {
        try {
          resolve(markdown.toHTML(source));
        } catch (e) {
          reject(e);
        }
      }
    }),

    processing: createProcessor({
      id: 'processing',
      target: 'javascript',
      extensions: ['pde'],
      url: jsbin.static + '/js/vendor/processing.min.js',
      init: function (ready) {
        $('#library').val( $('#library').find(':contains("Processing")').val() ).trigger('change');
        // init and expose jade
        $.getScript(jsbin.static + '/js/vendor/codemirror4/mode/clike/clike.js', ready);
      },
      handler: function (source, resolve, reject) {
        try {
          var sketch = Processing.compile(source).sourceCode;
          resolve([
            '(function(){',
            '  var canvas = document.querySelector("canvas");',
            '  if (!canvas) {',
            '    canvas = document.createElement("canvas");',
            '    (document.body || document.documentElement).appendChild(canvas);',
            '  }',
            '  canvas.width = window.innerWidth;',
            '  canvas.height = window.innerHeight;',
            '  var sketchProc = ' + sketch + ';',
            '  var p = new Processing(canvas, sketchProc);',
            '})();'
          ].join('\n'));
        } catch (e) {
          reject(e);
        }
      }
    }),

    jade: createProcessor({
      id: 'jade',
      target: 'html',
      extensions: ['jade'],
      url: jsbin.static + '/js/vendor/jade.js',
      init: function (ready) {
        // init and expose jade
        window.jade = require('jade');
        ready();
      },
      handler: function (source, resolve, reject) {
        try {
          resolve(jade.compile(source, { pretty: true })());
        } catch (e) {
          reject(e);
        }
      }
    }),

    less: createProcessor({
      id: 'less',
      target: 'css',
      extensions: ['less'],
      url: jsbin.static + '/js/vendor/less-1.4.2.min.js',
      init: function (ready) {
        // In CodeMirror 4, less is now included in the css mode, so no files to load
        ready();
      },
      handler: function (source, resolve, reject) {
        less.Parser().parse(source, function (err, result) {
          if (err) {
            return reject(err);
          }
          resolve(result.toCSS().trim());
        });
      }
    }),

    scss: createProcessor({
      id: 'scss',
      target: 'scss',
      extensions: ['scss'],
      // url: jsbin.static + '/js/vendor/sass/dist/sass.worker.js',
      init: function (ready) {
        // $.getScript(jsbin.static + '/js/vendor/codemirror3/mode/sass/sass.js', function () {
          // Sass.initialize(jsbin.static + '/js/vendor/sass/dist/worker.min.js');
        ready();
        // });
      },
      handler: function (source, resolve, reject) {
        $.ajax({
          type: 'post',
          url: '/processor',
          data: {
            language: 'sass-with-compass',
            source: source,
            url: jsbin.state.code,
            revision: jsbin.state.revision
          },
          success: function (data) {
            resolve(data);
          },
          error: function (jqxhr) {
            reject(jqxhr.responseText);
          }
        });
        // Sass.compile(source, function (result) {
        //   if (typeof result !== 'string') {
        //     reject(new Error('Error on line ' + result.line + ':\n' + result.message));
        //   } else {
        //     resolve(result.trim());
        //   }
        // });
      }
    }),

    stylus: createProcessor({
      id: 'stylus',
      target: 'css',
      extensions: ['styl'],
      url: jsbin.static + '/js/vendor/stylus.js',
      init: passthrough,
      handler: function (source, resolve, reject) {
        stylus(source).render(function (err, result) {
          if (err) {
            return reject(err);
          }

          resolve(result.trim());
        });
      }
    }),

    traceur: (function () {
      var SourceMapConsumer,
          SourceMapGenerator,
          ProjectWriter,
          ErrorReporter,
          hasError;
      return createProcessor({
        id: 'traceur',
        target: 'javascript',
        extensions: ['traceur'],
        url: jsbin.static + '/js/vendor/traceur.js',
        init: function (ready) {
          // Only create these once, when the processor is loaded
          $('#library').val( $('#library').find(':contains("Traceur")').val() ).trigger('change');
          SourceMapConsumer = traceur.outputgeneration.SourceMapConsumer;
          SourceMapGenerator = traceur.outputgeneration.SourceMapGenerator;
          ProjectWriter = traceur.outputgeneration.ProjectWriter;
          ErrorReporter = traceur.util.ErrorReporter;
          ready();
        },
        handler: function (source, resolve, reject) {
          hasError = false;

          var reporter = new ErrorReporter();
          reporter.reportMessageInternal = function(location, kind, format, args) {
            reject(new Error(ErrorReporter.format(location, format, args)));
          };

          var url = location.href;
          var project = new traceur.semantics.symbols.Project(url);
          var name = 'jsbin';

          var sourceFile = new traceur.syntax.SourceFile(name, source);
          project.addFile(sourceFile);
          var res = traceur.codegeneration.Compiler.compile(reporter, project, false);

          var msg = '/*\nIf you\'ve just translated to JS, make sure traceur is in the HTML panel.\nThis is terrible, sorry, but the only way we could get around race conditions.\n\nHugs & kisses,\nDave xox\n*/\ntry{window.traceur = top.traceur;}catch(e){}\n';
          resolve(msg + ProjectWriter.write(res));
        }
      });
    }())

  };

  var render = function() {
    if (jsbin.panels.ready) {
      editors.console.render();
    }
  };

  var $panelButtons = $('#panels');

  var $processorSelectors = $('div.processorSelector').each(function () {
    var panelId = this.getAttribute('data-type'),
        $el = $(this),
        $label = $el.closest('.label').find('strong a'),
        originalLabel = $label.text();

    $el.find('a').click(function (e) {
      var panel = jsbin.panels.panels[panelId];
      var $panelButton = $panelButtons.find('a[href$="' + panelId + '"]');

      e.preventDefault();
      var target = this.hash.substring(1),
          label = $(this).text(),
          code;
      if (target !== 'convert') {
        $panelButton.html(label);
        $label.text(label);
        if (target === panelId) {
          jsbin.processors.reset(panelId);
          render();
        } else {
          jsbin.processors.set(panelId, target, render);
        }
      } else {
        $label.text(originalLabel);
        $panelButton.html(originalLabel);
        panel.render().then(function (source) {
          jsbin.processors.reset(panelId);
          panel.setCode(source);
        });
      }
    }).bind('select', function (event, value) {
      if (value === this.hash.substring(1)) {
        var $panelButton = $panelButtons.find('a[href$="' + panelId + '"]');
        $label.text($(this).text());
        $panelButton.html($(this).text());
      }
    });
  });

  processors.set = function (panelId, processorName, callback) {
    var panel;

    // panelId can be id or instance of a panel.
    // this is kinda nasty, but it allows me to set panel processors during boot
    if (panelId instanceof Panel) {
      panel = panelId;
      panelId = panel.id;
    } else {
      panel = jsbin.panels.panels[panelId];
    }

    if (!jsbin.state.processors) {
      jsbin.state.processors = {};
    }

    var cmMode = processorName ? editorModes[processorName] || editorModes[panelId] : editorModes[panelId];

    // For JSX, use the plain JavaScript mode but disable smart indentation
    // because it doesn't work properly
    var smartIndent = cmMode !== 'jsx';
    cmMode = cmMode === 'jsx' ? 'javascript' : cmMode;

    // For less, the mode definition is changed in CodeMirror 4
    if (cmMode === 'less') {
      cmMode = 'text/x-less';
    }

    if (!panel) { return; }

    panel.trigger('processor', processorName || 'none');
    if (processorName && processors[processorName]) {
      jsbin.state.processors[panelId] = processorName;
      panel.processor = processors[processorName](function () {
        // processor is ready
        panel.editor.setOption('mode', cmMode);
        panel.editor.setOption('smartIndent', smartIndent);
        $processorSelectors.find('a').trigger('select', [processorName]);
        if (callback) { callback(); }
      });
    } else {
      // remove the preprocessor
      panel.editor.setOption('mode', cmMode);
      panel.editor.setOption('smartIndent', smartIndent);

      panel.processor = defaultProcessor;
      delete jsbin.state.processors[panelId];
      delete panel.type;
    }
  };

  processors.reset = function (panelId) {
    processors.set(panelId);
  };

  /**
   * Find the processor that uses the given file extension
   */
  processors.findByExtension = function (ext) {
    var id = processorBy.extension[ext];
    if (!id) return defaultProcessor;
    return jsbin.processors[id];
  };

  processors.by = processorBy;

  return processors;

}());
<commit_msg>Accept error messages (and show in console log for the moment)<commit_after>/*globals jsbin, _, $, RSVP*/
var processors = jsbin.processors = (function () {
  'use strict';
  /*
   * Debugging note: to emulate a slow connection, or a processor taking too
   * long to load, find the processor in question, and change the `init` method
   * to setTimeout(getScript, n seconds) - this will give you an idea of how
   * jsbin behaves when the processor isn't ready and the user makes calls to it
   */

  /**
   * Add properties to a function using underscore
   */
  var extendFn = function (fn, obj) {
    return _.extend(fn, obj);
  };

  var passthrough = function (ready) { return ready(); };
  var defaultProcessor = function (source) {
    return new RSVP.Promise(function (resolve) {
      resolve(source);
    });
  };

  /**
   * Cache extension ids by their file extensions
   */
  var processorBy = {
    extension: {}
  };

  /**
   * Create a processor – accepts an object containing:
   *
   *    id          Processor name. Required.
   *    target      The target panel. Optional - defaults to the id.
   *    extensions  Possible file extensions for this processor (for gist i/o).
   *                Optional. Defaults to the id.
   *    url         URL of the loader script file. Optional.
   *    init        Setup the processor here. Optional – defaults to the
   *                passthrough (above).
   *    handler     Where the magic happens. Do all processing in here.
   *                Optional - defaults to the defaultProcessor (above).
   */
  var createProcessor = function (opts) {
    var url = opts.url,
        init = opts.init || passthrough,
        handler = opts.handler || defaultProcessor,
        processorData = _.pick(opts, 'id', 'target', 'extensions');

    opts.extensions = opts.extensions || [];
    if (!opts.extensions.length) opts.extensions = [opts.id];

    opts.extensions.forEach(function (ext) {
      processorBy.extension[ext] = opts.id;
    });

    // This actually loads in the processor – script files & init code
    var loadProcessor = function (ready) {

      var failed = false;

      // Overwritten when the script loads
      var callback = function () {
        window.console && window.console.warn('Processor is not ready yet - trying again');
        failed = true;
        return '';
      };

      // Script has loaded.
      // Run any init code, and swap the callback. If we failed, try again.
      var scriptCB = function () {
        init(function () {
          callback = handler;
          if (failed) {
            renderLivePreview();
          }
          ready();
        });
      };

      if (url) {
        // Load the processor's script
        $.getScript(url, scriptCB);
      } else {
        // No url, go straight on
        init(function () {
          callback = handler;
          ready();
        });
      }

      // Create a proxy function that holds the handler in scope so that, when
      // the callbacks are swapped, rendering still works.
      var cache = {
        source: '',
        result: ''
      };
      var proxyCallback = function (source) {
        return new RSVP.Promise(function (resolve, reject) {
          source = source.trim();
          if (source === cache.source) {
            resolve(cache.result);
          } else {
            callback(source, function (result) {
              cache.source = source;
              cache.result = result;
              resolve(result);
            }, reject);
          }
        });
      };

      // Return the method that will be used to render
      return extendFn(proxyCallback, processorData);
    };

    // Processor fucntion also has the important data on it
    return extendFn(loadProcessor, processorData);
  };

  /**
   * JS Bin's processors
   */
  var processors = {

    html: createProcessor({
      id: 'html'
    }),

    css: createProcessor({
      id: 'css'
    }),

    javascript: createProcessor({
      id: 'javascript',
      extensions: ['js']
    }),

    coffeescript: createProcessor({
      id: 'coffeescript',
      target: 'javascript',
      extensions: ['coffee'],
      url: jsbin.static + '/js/vendor/coffee-script.js',
      init: function (ready) {
        $.getScript(jsbin.static + '/js/vendor/codemirror4/mode/coffeescript/coffeescript.js', ready);
      },
      handler: function (source, resolve, reject) {
        var renderedCode = '';
        try {
          renderedCode = CoffeeScript.compile(source, {
            bare: true
          });
          resolve(renderedCode);
        } catch (e) {
          reject(e);
        }
      }
    }),

    jsx: createProcessor({
      id: 'jsx',
      target: 'javascript',
      extensions: ['jsx'],
      url: jsbin.static + '/js/vendor/JSXTransformer.js',
      init: function (ready) {
        // Don't add React if the code already contains a script whose name
        // starts with 'react', to avoid duplicate copies.
        var code = editors.html.getCode();
        if (!(/<script[^>]*src=\S*\breact\b/i).test(code)) {
          $('#library').val( $('#library').find(':contains("React with Add-Ons")').val() ).trigger('change');
        }
        ready();
      },
      handler: function (source, resolve, reject) {
        var renderedCode = '';
        try {
          renderedCode = JSXTransformer.transform(source).code;
          resolve(renderedCode);
        } catch (e) {
          reject(e);
        }
      }
    }),

    typescript: createProcessor({
      id: 'typescript',
      target: 'javascript',
      extensions: ['ts'],
      url: jsbin.static + '/js/vendor/typescript.min.js',
      init: passthrough,
      handler: function (source, resolve, reject) {
        var noop = function () {};
        var outfile = {
          source: '',
          Write: function (s) {
            this.source += s;
          },
          WriteLine: function (s) {
            this.source += s + '\n';
          },
          Close: noop
        };

        var outerr = {
          Write: noop,
          WriteLine: noop,
          Close: noop
        };

        var parseErrors = [];

        var compiler = new TypeScript.TypeScriptCompiler(outfile, outerr);

        compiler.setErrorCallback(function (start, len, message) {
          parseErrors.push({ start: start, len: len, message: message });
        });
        compiler.parser.errorRecovery = true;

        compiler.addUnit(source, 'jsbin.ts');
        compiler.typeCheck();
        compiler.reTypeCheck();
        compiler.emit();

        for (var i = 0, len = parseErrors.length; i < len; i++) {
          console.log('Error Message: ' + parseErrors[i].message);
          console.log('Error Start: ' + parseErrors[i].start);
          console.log('Error Length: ' + parseErrors[i].len);
        }

        if (parseErrors.length) {
          reject();
        } else {
          resolve(outfile.source);
        }
      }
    }),

    markdown: createProcessor({
      id: 'markdown',
      target: 'html',
      extensions: ['md', 'markdown', 'mdown'],
      url: jsbin.static + '/js/vendor/markdown.js',
      init: function (ready) {
        $.getScript(jsbin.static + '/js/vendor/codemirror4/mode/markdown/markdown.js', ready);
      },
      handler: function (source, resolve, reject) {
        try {
          resolve(markdown.toHTML(source));
        } catch (e) {
          reject(e);
        }
      }
    }),

    processing: createProcessor({
      id: 'processing',
      target: 'javascript',
      extensions: ['pde'],
      url: jsbin.static + '/js/vendor/processing.min.js',
      init: function (ready) {
        $('#library').val( $('#library').find(':contains("Processing")').val() ).trigger('change');
        // init and expose jade
        $.getScript(jsbin.static + '/js/vendor/codemirror4/mode/clike/clike.js', ready);
      },
      handler: function (source, resolve, reject) {
        try {
          var sketch = Processing.compile(source).sourceCode;
          resolve([
            '(function(){',
            '  var canvas = document.querySelector("canvas");',
            '  if (!canvas) {',
            '    canvas = document.createElement("canvas");',
            '    (document.body || document.documentElement).appendChild(canvas);',
            '  }',
            '  canvas.width = window.innerWidth;',
            '  canvas.height = window.innerHeight;',
            '  var sketchProc = ' + sketch + ';',
            '  var p = new Processing(canvas, sketchProc);',
            '})();'
          ].join('\n'));
        } catch (e) {
          reject(e);
        }
      }
    }),

    jade: createProcessor({
      id: 'jade',
      target: 'html',
      extensions: ['jade'],
      url: jsbin.static + '/js/vendor/jade.js',
      init: function (ready) {
        // init and expose jade
        window.jade = require('jade');
        ready();
      },
      handler: function (source, resolve, reject) {
        try {
          resolve(jade.compile(source, { pretty: true })());
        } catch (e) {
          reject(e);
        }
      }
    }),

    less: createProcessor({
      id: 'less',
      target: 'css',
      extensions: ['less'],
      url: jsbin.static + '/js/vendor/less-1.4.2.min.js',
      init: function (ready) {
        // In CodeMirror 4, less is now included in the css mode, so no files to load
        ready();
      },
      handler: function (source, resolve, reject) {
        less.Parser().parse(source, function (err, result) {
          if (err) {
            return reject(err);
          }
          resolve(result.toCSS().trim());
        });
      }
    }),

    scss: createProcessor({
      id: 'scss',
      target: 'scss',
      extensions: ['scss'],
      // url: jsbin.static + '/js/vendor/sass/dist/sass.worker.js',
      init: function (ready) {
        // $.getScript(jsbin.static + '/js/vendor/codemirror3/mode/sass/sass.js', function () {
          // Sass.initialize(jsbin.static + '/js/vendor/sass/dist/worker.min.js');
        ready();
        // });
      },
      handler: function (source, resolve, reject) {
        $.ajax({
          type: 'post',
          url: '/processor',
          data: {
            language: 'sass-with-compass',
            source: source,
            url: jsbin.state.code,
            revision: jsbin.state.revision
          },
          success: function (data) {
            if (data.errors) {
              console.log(data.errors);
            } else if (data.result) {
              resolve(data.result);
            }
          },
          error: function (jqxhr) {
            reject(jqxhr.responseText);
          }
        });
        // Sass.compile(source, function (result) {
        //   if (typeof result !== 'string') {
        //     reject(new Error('Error on line ' + result.line + ':\n' + result.message));
        //   } else {
        //     resolve(result.trim());
        //   }
        // });
      }
    }),

    stylus: createProcessor({
      id: 'stylus',
      target: 'css',
      extensions: ['styl'],
      url: jsbin.static + '/js/vendor/stylus.js',
      init: passthrough,
      handler: function (source, resolve, reject) {
        stylus(source).render(function (err, result) {
          if (err) {
            return reject(err);
          }

          resolve(result.trim());
        });
      }
    }),

    traceur: (function () {
      var SourceMapConsumer,
          SourceMapGenerator,
          ProjectWriter,
          ErrorReporter,
          hasError;
      return createProcessor({
        id: 'traceur',
        target: 'javascript',
        extensions: ['traceur'],
        url: jsbin.static + '/js/vendor/traceur.js',
        init: function (ready) {
          // Only create these once, when the processor is loaded
          $('#library').val( $('#library').find(':contains("Traceur")').val() ).trigger('change');
          SourceMapConsumer = traceur.outputgeneration.SourceMapConsumer;
          SourceMapGenerator = traceur.outputgeneration.SourceMapGenerator;
          ProjectWriter = traceur.outputgeneration.ProjectWriter;
          ErrorReporter = traceur.util.ErrorReporter;
          ready();
        },
        handler: function (source, resolve, reject) {
          hasError = false;

          var reporter = new ErrorReporter();
          reporter.reportMessageInternal = function(location, kind, format, args) {
            reject(new Error(ErrorReporter.format(location, format, args)));
          };

          var url = location.href;
          var project = new traceur.semantics.symbols.Project(url);
          var name = 'jsbin';

          var sourceFile = new traceur.syntax.SourceFile(name, source);
          project.addFile(sourceFile);
          var res = traceur.codegeneration.Compiler.compile(reporter, project, false);

          var msg = '/*\nIf you\'ve just translated to JS, make sure traceur is in the HTML panel.\nThis is terrible, sorry, but the only way we could get around race conditions.\n\nHugs & kisses,\nDave xox\n*/\ntry{window.traceur = top.traceur;}catch(e){}\n';
          resolve(msg + ProjectWriter.write(res));
        }
      });
    }())

  };

  var render = function() {
    if (jsbin.panels.ready) {
      editors.console.render();
    }
  };

  var $panelButtons = $('#panels');

  var $processorSelectors = $('div.processorSelector').each(function () {
    var panelId = this.getAttribute('data-type'),
        $el = $(this),
        $label = $el.closest('.label').find('strong a'),
        originalLabel = $label.text();

    $el.find('a').click(function (e) {
      var panel = jsbin.panels.panels[panelId];
      var $panelButton = $panelButtons.find('a[href$="' + panelId + '"]');

      e.preventDefault();
      var target = this.hash.substring(1),
          label = $(this).text(),
          code;
      if (target !== 'convert') {
        $panelButton.html(label);
        $label.text(label);
        if (target === panelId) {
          jsbin.processors.reset(panelId);
          render();
        } else {
          jsbin.processors.set(panelId, target, render);
        }
      } else {
        $label.text(originalLabel);
        $panelButton.html(originalLabel);
        panel.render().then(function (source) {
          jsbin.processors.reset(panelId);
          panel.setCode(source);
        });
      }
    }).bind('select', function (event, value) {
      if (value === this.hash.substring(1)) {
        var $panelButton = $panelButtons.find('a[href$="' + panelId + '"]');
        $label.text($(this).text());
        $panelButton.html($(this).text());
      }
    });
  });

  processors.set = function (panelId, processorName, callback) {
    var panel;

    // panelId can be id or instance of a panel.
    // this is kinda nasty, but it allows me to set panel processors during boot
    if (panelId instanceof Panel) {
      panel = panelId;
      panelId = panel.id;
    } else {
      panel = jsbin.panels.panels[panelId];
    }

    if (!jsbin.state.processors) {
      jsbin.state.processors = {};
    }

    var cmMode = processorName ? editorModes[processorName] || editorModes[panelId] : editorModes[panelId];

    // For JSX, use the plain JavaScript mode but disable smart indentation
    // because it doesn't work properly
    var smartIndent = cmMode !== 'jsx';
    cmMode = cmMode === 'jsx' ? 'javascript' : cmMode;

    // For less, the mode definition is changed in CodeMirror 4
    if (cmMode === 'less') {
      cmMode = 'text/x-less';
    }

    if (!panel) { return; }

    panel.trigger('processor', processorName || 'none');
    if (processorName && processors[processorName]) {
      jsbin.state.processors[panelId] = processorName;
      panel.processor = processors[processorName](function () {
        // processor is ready
        panel.editor.setOption('mode', cmMode);
        panel.editor.setOption('smartIndent', smartIndent);
        $processorSelectors.find('a').trigger('select', [processorName]);
        if (callback) { callback(); }
      });
    } else {
      // remove the preprocessor
      panel.editor.setOption('mode', cmMode);
      panel.editor.setOption('smartIndent', smartIndent);

      panel.processor = defaultProcessor;
      delete jsbin.state.processors[panelId];
      delete panel.type;
    }
  };

  processors.reset = function (panelId) {
    processors.set(panelId);
  };

  /**
   * Find the processor that uses the given file extension
   */
  processors.findByExtension = function (ext) {
    var id = processorBy.extension[ext];
    if (!id) return defaultProcessor;
    return jsbin.processors[id];
  };

  processors.by = processorBy;

  return processors;

}());
<|endoftext|>


<commit_before>/*
    SequenceServer's BLAST module/method

    Adds `blast` method to `SS`.

    blast():
        not implemented

    `blast` defines the following methods:
        init():
           initialize `blast` object

    This module defines the following events:

        sequence_type_changed:
            When the type (nucleotide or protein) of the input sequence changes.

    TODO: should (probably) access UI objects through some sort of interface
*/
SS.blast = (function () {
    /* private methods */

    /*
        determine input sequence type, store it, and trigger
        'sequence_type_changed' event if the input sequence has changed
        (TODO: the method is doing too much; split it)
    */
    var signal_sequence_type_changed = function () {
        var prev_seq = prev_seq_type = '';

        (function poll () {
            setTimeout(function (){
                var seq, seq_type;
                seq = $('#sequence').val();

                //act only if user input has changed
                if (seq != prev_seq){
                    prev_seq = seq;

                    //get input sequence type from the server
                    $.post('', {sequence: seq}, function(seq_type){
                        if (seq_type != prev_seq_type){
                            prev_seq_type = seq_type;

                            //store sequence type and notify listeners
                            $('#sequence').data('sequence_type', seq_type).trigger('sequence_type_changed');
                        }
                    });
                }
                poll();
              }, 100)
        }());
    };

    /* public interface */

    var blast = function () {
        return undefined;
    }

    blast.init = function () {
        signal_sequence_type_changed();
    }

    return blast;
}());
<commit_msg>SS.blast: trigger `change` event if the input sequence has changed<commit_after>/*
    SequenceServer's BLAST module/method

    Adds `blast` method to `SS`.

    blast():
        not implemented

    `blast` defines the following methods:
        init():
           initialize `blast` object

    This module defines the following events:

        sequence_type_changed:
            When the type (nucleotide or protein) of the input sequence changes.

    TODO: should (probably) access UI objects through some sort of interface
*/
SS.blast = (function () {
    /* private methods */

    /*
        determine input sequence type, store it, and trigger
        'sequence_type_changed' event if the input sequence has changed
        (TODO: the method is doing too much; split it)
    */
    var signal_sequence_type_changed = function () {
        var prev_seq = prev_seq_type = '';

        (function poll () {
            setTimeout(function (){
                var seq, seq_type;
                seq = $('#sequence').val();

                //act only if user input has changed
                if (seq != prev_seq){
                    prev_seq = seq;
                    $('#sequence').change();

                    //get input sequence type from the server
                    $.post('', {sequence: seq}, function(seq_type){
                        if (seq_type != prev_seq_type){
                            prev_seq_type = seq_type;

                            //store sequence type and notify listeners
                            $('#sequence').data('sequence_type', seq_type).trigger('sequence_type_changed');
                        }
                    });
                }
                poll();
              }, 100)
        }());
    };

    /* public interface */

    var blast = function () {
        return undefined;
    }

    blast.init = function () {
        signal_sequence_type_changed();
    }

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


<commit_before>'use strict';

describe('parseOptions', function () {

  var $compile, scope, $parseOptions;

  beforeEach(module('mgcrea.ngStrap.helpers.parseOptions'));

  beforeEach(inject(function (_$rootScope_, _$compile_, _$parseOptions_) {
    scope = _$rootScope_;
    $compile = _$compile_;
    $parseOptions = _$parseOptions_;
  }));

  function getParsedValues(parsedOptions) {
      var parsedValues = null;

      parsedOptions.valuesFn(scope, null)
        .then(function(values) {
          parsedValues = values;
        });

      scope.$digest();

      return parsedValues;
  }

  // Tests
  function generateParsedValuesLengthTests(parsedValues, optionsArray) {
      expect(parsedValues).toBeDefined();
      expect(parsedValues.length).toBe(optionsArray.length);
  }

  describe('for array data sources', function () {
    describe('with "label for value in array" format', function() {

      it('should support using objects', function() {
        scope.colors = [
          {name:'black', shade:'dark'},
          {name:'white', shade:'light'},
          {name:'red', shade:'dark'},
          {name:'blue', shade:'dark'},
          {name:'yellow', shade:'light'},
          {name:'false', shade:false},
          {name:'', shade:''}
        ];

        var parsedOptions = $parseOptions('color.name for color in colors');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.colors);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.colors[v.index].name);
          expect(v.value).toBe(scope.colors[v.index]);
        });
      });

      it('should support basic value types', function() {
        scope.values = [
          'true',
          'false',
          true,
          false,
          0,
          1,
          14.5,
          18.3,
          0.0,
          -1,
          new Date()
        ];
        var parsedOptions = $parseOptions('val for val in values');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support boolean values', function() {
        scope.values = [
          true,
          false
        ];
        var parsedOptions = $parseOptions('val for val in values');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support null/undefined/empty string values', function() {
        scope.values = [
          'true',
          undefined,
          'false',
          '',
          null
        ];
        var parsedOptions = $parseOptions('val for val in values');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support filtered values', function() {
        scope.values = [
          'foo',
          'bar',
          'baz'
        ];
        var parsedOptions = $parseOptions('val for val in values | filter:b:startsWith');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support incorrect filtered values', function() {
        scope.values = {};
        var parsedOptions = $parseOptions('val for val in values | filter:b:startsWith');
        var parsedValues = getParsedValues(parsedOptions);
        expect(parsedValues).toEqual([]);
      });

    });

    describe('with "select as label for value in array" format', function() {

      it('should support using objects', function() {
        scope.colors = [
          {name:'black', shade:'dark'},
          {name:'white', shade:'light'},
          {name:'red', shade:'dark'},
          {name:'blue', shade:'dark'},
          {name:'yellow', shade:'light'},
          {name:'false', shade:false},
          {name:'', shade:''}
        ];

        var parsedOptions = $parseOptions('color.shade as color.name for color in colors');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.colors);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.colors[v.index].name);
          expect(v.value).toBe(scope.colors[v.index].shade);
        });
      });

    });

    //
    // TODO: looks like group by it is not implemented yet ?
    //
    // describe('with "label group by group for value in array" format', function() {
    //
    //   it('should support using objects', function() {
    //     scope.colors = [
    //       {name:'black', shade:'dark'},
    //       {name:'white', shade:'light'},
    //       {name:'red', shade:'dark'},
    //       {name:'blue', shade:'dark'},
    //       {name:'yellow', shade:'light'},
    //       {name:'false', shade:false},
    //       {name:'', shade:''}
    //     ];
    //
    //     var parsedOptions = $parseOptions('color.name group by color.shade for color in colors');
    //     var parsedValues = getParsedValues(parsedOptions);
    //     // generateParsedValuesLengthTests(parsedValues, scope.colors);
    //
    //     // angular.forEach(parsedValues, function(v) {
    //     //   expect(v.label).toBe(scope.colors[v.index].name);
    //     //   expect(v.value).toBe(scope.colors[v.index].shade);
    //     // });
    //   });
    //
    // });

  });

  //
  // TODO: looks like this format is not implemented yet
  //
  // describe('for object data sources', function () {
  //   describe('with "label for (key , value) in object" format', function() {
  //
  //     it('should support using objects', function() {
  //       scope.colors = {
  //         black: 'Black',
  //         white: 'White',
  //         red: 'Red',
  //         blue: 'Blue',
  //         yellow: 'Yellow',
  //         none: false
  //       };
  //
  //       var parsedOptions = $parseOptions('value for (key, value) in colors');
  //       var parsedValues = getParsedValues(parsedOptions);
  //       generateParsedValuesLengthTests(parsedValues, scope.colors);
  //     });
  //
  //   });
  //
  // });
});
<commit_msg>chore(parse-options): remove invalid test<commit_after>'use strict';

describe('parseOptions', function () {

  var $compile, scope, $parseOptions;

  beforeEach(module('mgcrea.ngStrap.helpers.parseOptions'));

  beforeEach(inject(function (_$rootScope_, _$compile_, _$parseOptions_) {
    scope = _$rootScope_;
    $compile = _$compile_;
    $parseOptions = _$parseOptions_;
  }));

  function getParsedValues(parsedOptions) {
      var parsedValues = null;

      parsedOptions.valuesFn(scope, null)
        .then(function(values) {
          parsedValues = values;
        });

      scope.$digest();

      return parsedValues;
  }

  // Tests
  function generateParsedValuesLengthTests(parsedValues, optionsArray) {
      expect(parsedValues).toBeDefined();
      expect(parsedValues.length).toBe(optionsArray.length);
  }

  describe('for array data sources', function () {
    describe('with "label for value in array" format', function() {

      it('should support using objects', function() {
        scope.colors = [
          {name:'black', shade:'dark'},
          {name:'white', shade:'light'},
          {name:'red', shade:'dark'},
          {name:'blue', shade:'dark'},
          {name:'yellow', shade:'light'},
          {name:'false', shade:false},
          {name:'', shade:''}
        ];

        var parsedOptions = $parseOptions('color.name for color in colors');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.colors);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.colors[v.index].name);
          expect(v.value).toBe(scope.colors[v.index]);
        });
      });

      it('should support basic value types', function() {
        scope.values = [
          'true',
          'false',
          true,
          false,
          0,
          1,
          14.5,
          18.3,
          0.0,
          -1,
          new Date()
        ];
        var parsedOptions = $parseOptions('val for val in values');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support boolean values', function() {
        scope.values = [
          true,
          false
        ];
        var parsedOptions = $parseOptions('val for val in values');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support null/undefined/empty string values', function() {
        scope.values = [
          'true',
          undefined,
          'false',
          '',
          null
        ];
        var parsedOptions = $parseOptions('val for val in values');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

      it('should support filtered values', function() {
        scope.values = [
          'foo',
          'bar',
          'baz'
        ];
        var parsedOptions = $parseOptions('val for val in values | filter:b:startsWith');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.values);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.values[v.index]);
          expect(v.value).toBe(scope.values[v.index]);
        });
      });

    });

    describe('with "select as label for value in array" format', function() {

      it('should support using objects', function() {
        scope.colors = [
          {name:'black', shade:'dark'},
          {name:'white', shade:'light'},
          {name:'red', shade:'dark'},
          {name:'blue', shade:'dark'},
          {name:'yellow', shade:'light'},
          {name:'false', shade:false},
          {name:'', shade:''}
        ];

        var parsedOptions = $parseOptions('color.shade as color.name for color in colors');
        var parsedValues = getParsedValues(parsedOptions);
        generateParsedValuesLengthTests(parsedValues, scope.colors);

        angular.forEach(parsedValues, function(v) {
          expect(v.label).toBe(scope.colors[v.index].name);
          expect(v.value).toBe(scope.colors[v.index].shade);
        });
      });

    });

    //
    // TODO: looks like group by it is not implemented yet ?
    //
    // describe('with "label group by group for value in array" format', function() {
    //
    //   it('should support using objects', function() {
    //     scope.colors = [
    //       {name:'black', shade:'dark'},
    //       {name:'white', shade:'light'},
    //       {name:'red', shade:'dark'},
    //       {name:'blue', shade:'dark'},
    //       {name:'yellow', shade:'light'},
    //       {name:'false', shade:false},
    //       {name:'', shade:''}
    //     ];
    //
    //     var parsedOptions = $parseOptions('color.name group by color.shade for color in colors');
    //     var parsedValues = getParsedValues(parsedOptions);
    //     // generateParsedValuesLengthTests(parsedValues, scope.colors);
    //
    //     // angular.forEach(parsedValues, function(v) {
    //     //   expect(v.label).toBe(scope.colors[v.index].name);
    //     //   expect(v.value).toBe(scope.colors[v.index].shade);
    //     // });
    //   });
    //
    // });

  });

  //
  // TODO: looks like this format is not implemented yet
  //
  // describe('for object data sources', function () {
  //   describe('with "label for (key , value) in object" format', function() {
  //
  //     it('should support using objects', function() {
  //       scope.colors = {
  //         black: 'Black',
  //         white: 'White',
  //         red: 'Red',
  //         blue: 'Blue',
  //         yellow: 'Yellow',
  //         none: false
  //       };
  //
  //       var parsedOptions = $parseOptions('value for (key, value) in colors');
  //       var parsedValues = getParsedValues(parsedOptions);
  //       generateParsedValuesLengthTests(parsedValues, scope.colors);
  //     });
  //
  //   });
  //
  // });
});
<|endoftext|>


<commit_before>/**
 * @description Google Chart Api Directive Module for AngularJS
 * @version 0.0.9
 * @author Nicolas Bouillon <nicolas@bouil.org>
 * @author GitHub contributors
 * @license MIT
 * @year 2013
 */
(function (document, window, angular) {
    'use strict';

    angular.module('googlechart', [])

        .constant('googleChartApiConfig', {
            version: '1',
            optionalSettings: {
                packages: ['corechart']
            }
        })

        .provider('googleJsapiUrl', function () {
            var protocol = 'https:';
            var url = '//www.google.com/jsapi';

            this.setProtocol = function(newProtocol) {
                protocol = newProtocol;
            };

            this.setUrl = function(newUrl) {
                url = newUrl;
            };

            this.$get = function() {
                return (protocol ? protocol : '') + url;
            };
        })
        .factory('googleChartApiPromise', ['$rootScope', '$q', 'googleChartApiConfig', 'googleJsapiUrl', function ($rootScope, $q, apiConfig, googleJsapiUrl) {
            var apiReady = $q.defer();
            var onLoad = function () {
                // override callback function
                var settings = {
                    callback: function () {
                        var oldCb = apiConfig.optionalSettings.callback;
                        $rootScope.$apply(function () {
                            apiReady.resolve();
                        });

                        if (angular.isFunction(oldCb)) {
                            oldCb.call(this);
                        }
                    }
                };

                settings = angular.extend({}, apiConfig.optionalSettings, settings);

                window.google.load('visualization', apiConfig.version, settings);
            };
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');

            script.setAttribute('type', 'text/javascript');
            script.src = googleJsapiUrl;

            if (script.addEventListener) { // Standard browsers (including IE9+)
                script.addEventListener('load', onLoad, false);
            } else { // IE8 and below
                script.onreadystatechange = function () {
                    if (script.readyState === 'loaded' || script.readyState === 'complete') {
                        script.onreadystatechange = null;
                        onLoad();
                    }
                };
            }

            head.appendChild(script);

            return apiReady.promise;
        }])
        .directive('googleChart', ['$timeout', '$window', '$rootScope', 'googleChartApiPromise', function ($timeout, $window, $rootScope, googleChartApiPromise) {
            return {
                restrict: 'A',
                scope: {
                    chart: '=chart',
                    onReady: '&',
                    onSelect: '&',
                    select: '&'
                },
                link: function ($scope, $elm, $attrs) {
                    /* Watches, to refresh the chart when its data, formatters, options, or type change.
                        All other values intentionally disregarded to avoid double calls to the draw
                        function. Please avoid making changes to these objects directly from this directive.*/
                    $scope.$watch(function () {
                        if ($scope.chart) {
                            return {
                                data: $scope.chart.data,
                                formatters: $scope.chart.formatters,
                                options: $scope.chart.options,
                                type: $scope.chart.type,
                                customFormatters: $scope.chart.customFormatters
                            };
                        }
                        return $scope.chart;
                    }, function () {
                        drawAsync();
                    }, true); // true is for deep object equality checking

                    // Redraw the chart if the window is resized
                    $rootScope.$on('resizeMsg', function () {
                        $timeout(function () {
                            // Not always defined yet in IE so check
                            if($scope.chartWrapper) {
                                drawAsync();
                            }
                        });
                    });

                    // Keeps old formatter configuration to compare against
                    $scope.oldChartFormatters = {};

                    function applyFormat(formatType, formatClass, dataTable) {

                        if (typeof($scope.chart.formatters[formatType]) != 'undefined') {
                            if (!angular.equals($scope.chart.formatters[formatType], $scope.oldChartFormatters[formatType])) {
                                $scope.oldChartFormatters[formatType] = $scope.chart.formatters[formatType];
                                $scope.formatters[formatType] = [];

                                if (formatType === 'color') {
                                    for (var cIdx = 0; cIdx < $scope.chart.formatters[formatType].length; cIdx++) {
                                        var colorFormat = new formatClass();

                                        for (i = 0; i < $scope.chart.formatters[formatType][cIdx].formats.length; i++) {
                                            var data = $scope.chart.formatters[formatType][cIdx].formats[i];

                                            if (typeof(data.fromBgColor) != 'undefined' && typeof(data.toBgColor) != 'undefined')
                                                colorFormat.addGradientRange(data.from, data.to, data.color, data.fromBgColor, data.toBgColor);
                                            else
                                                colorFormat.addRange(data.from, data.to, data.color, data.bgcolor);
                                        }

                                        $scope.formatters[formatType].push(colorFormat)
                                    }
                                } else {

                                    for (var i = 0; i < $scope.chart.formatters[formatType].length; i++) {
                                        $scope.formatters[formatType].push(new formatClass(
                                            $scope.chart.formatters[formatType][i])
                                        );
                                    }
                                }
                            }

                            //apply formats to dataTable
                            for (i = 0; i < $scope.formatters[formatType].length; i++) {
                                if ($scope.chart.formatters[formatType][i].columnNum < dataTable.getNumberOfColumns())
                                    $scope.formatters[formatType][i].format(dataTable, $scope.chart.formatters[formatType][i].columnNum);
                            }

                            //Many formatters require HTML tags to display special formatting
                            if (formatType === 'arrow' || formatType === 'bar' || formatType === 'color')
                                $scope.chart.options.allowHtml = true;
                        }
                    }

                    function draw() {
                        if (!draw.triggered && ($scope.chart != undefined)) {
                            draw.triggered = true;
                            $timeout(function () {

                                if (typeof($scope.chartWrapper) == 'undefined') {
                                    var chartWrapperArgs = {
                                        chartType: $scope.chart.type,
                                        dataTable: $scope.chart.data,
                                        view: $scope.chart.view,
                                        options: $scope.chart.options,
                                        containerId: $elm[0]
                                    };

                                    $scope.chartWrapper = new google.visualization.ChartWrapper(chartWrapperArgs);
                                    google.visualization.events.addListener($scope.chartWrapper, 'ready', function () {
                                        $scope.chart.displayed = true;
                                        $scope.$apply(function (scope) {
                                            scope.onReady({chartWrapper: scope.chartWrapper});
                                        });
                                    });
                                    google.visualization.events.addListener($scope.chartWrapper, 'error', function (err) {
                                        console.log("Chart not displayed due to error: " + err.message + ". Full error object follows.");
                                        console.log(err);
                                    });
                                    google.visualization.events.addListener($scope.chartWrapper, 'select', function () {
                                        var selectedItem = $scope.chartWrapper.getChart().getSelection()[0];
                                        $scope.$apply(function () {
                                            if ($attrs.select) {
                                                console.log('Angular-Google-Chart: The \'select\' attribute is deprecated and will be removed in a future release.  Please use \'onSelect\'.');
                                                $scope.select({ selectedItem: selectedItem });
                                            }
                                            else {
                                                $scope.onSelect({ selectedItem: selectedItem });
                                            }
                                        });
                                    });
                                }

                                if (typeof($scope.formatters) === 'undefined')
                                    $scope.formatters = {};

                                if (typeof($scope.chart.formatters) != 'undefined') {
                                    applyFormat("number", google.visualization.NumberFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("arrow", google.visualization.ArrowFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("date", google.visualization.DateFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("bar", google.visualization.BarFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("color", google.visualization.ColorFormat, $scope.chartWrapper.getDataTable());
                                }

                                var customFormatters = $scope.chart.customFormatters;
                                if (typeof(customFormatters) != 'undefined') {
                                    for (var name in customFormatters) {
                                        applyFormat(name, customFormatters[name], $scope.chartWrapper.getDataTable());
                                    }
                                }

                                $timeout(function () {
                                    $scope.chartWrapper.draw();
                                    draw.triggered = false;
                                });
                            }, 0, true);
                        }
                    }

                    function drawAsync() {
                        googleChartApiPromise.then(function () {
                            draw();
                        })
                    }
                }
            };
        }])

        .run(['$rootScope', '$window', function ($rootScope, $window) {
            angular.element($window).bind('resize', function () {
                $rootScope.$emit('resizeMsg');
            });
        }]);

})(document, window, window.angular);
<commit_msg>Dashboard: Latest ng-google-chart.<commit_after>/**
 * @description Google Chart Api Directive Module for AngularJS
 * @version 0.0.9
 * @author Nicolas Bouillon <nicolas@bouil.org>
 * @author GitHub contributors
 * @license MIT
 * @year 2013
 */
(function (document, window, angular) {
    'use strict';

    angular.module('googlechart', [])

        .constant('googleChartApiConfig', {
            version: '1',
            optionalSettings: {
                packages: ['corechart']
            }
        })

        .provider('googleJsapiUrl', function () {
            var protocol = 'https:';
            var url = '//www.google.com/jsapi';

            this.setProtocol = function(newProtocol) {
                protocol = newProtocol;
            };

            this.setUrl = function(newUrl) {
                url = newUrl;
            };

            this.$get = function() {
                return (protocol ? protocol : '') + url;
            };
        })
        .factory('googleChartApiPromise', ['$rootScope', '$q', 'googleChartApiConfig', 'googleJsapiUrl', function ($rootScope, $q, apiConfig, googleJsapiUrl) {
            var apiReady = $q.defer();
            var onLoad = function () {
                // override callback function
                var settings = {
                    callback: function () {
                        var oldCb = apiConfig.optionalSettings.callback;
                        $rootScope.$apply(function () {
                            apiReady.resolve();
                        });

                        if (angular.isFunction(oldCb)) {
                            oldCb.call(this);
                        }
                    }
                };

                settings = angular.extend({}, apiConfig.optionalSettings, settings);

                window.google.load('visualization', apiConfig.version, settings);
            };
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');

            script.setAttribute('type', 'text/javascript');
            script.src = googleJsapiUrl;

            if (script.addEventListener) { // Standard browsers (including IE9+)
                script.addEventListener('load', onLoad, false);
            } else { // IE8 and below
                script.onreadystatechange = function () {
                    if (script.readyState === 'loaded' || script.readyState === 'complete') {
                        script.onreadystatechange = null;
                        onLoad();
                    }
                };
            }

            head.appendChild(script);

            return apiReady.promise;
        }])
        .directive('googleChart', ['$timeout', '$window', '$rootScope', 'googleChartApiPromise', function ($timeout, $window, $rootScope, googleChartApiPromise) {
            return {
                restrict: 'A',
                scope: {
                    chart: '=chart',
                    onReady: '&',
                    onSelect: '&',
                    select: '&'
                },
                link: function ($scope, $elm, $attrs) {
                    /* Watches, to refresh the chart when its data, formatters, options, or type change.
                        All other values intentionally disregarded to avoid double calls to the draw
                        function. Please avoid making changes to these objects directly from this directive.*/
                    $scope.$watch(function () {
                        if ($scope.chart) {
                            return {
                                data: $scope.chart.data,
                                formatters: $scope.chart.formatters,
                                options: $scope.chart.options,
                                type: $scope.chart.type,
                                customFormatters: $scope.chart.customFormatters
                            };
                        }
                        return $scope.chart;
                    }, function () {
                        drawAsync();
                    }, true); // true is for deep object equality checking

                    // Redraw the chart if the window is resized
                    $rootScope.$on('resizeMsg', function () {
                        $timeout(function () {
                            // Not always defined yet in IE so check
                            if($scope.chartWrapper) {
                                drawAsync();
                            }
                        });
                    });

                    // Keeps old formatter configuration to compare against
                    $scope.oldChartFormatters = {};

                    function applyFormat(formatType, formatClass, dataTable) {

                        if (typeof($scope.chart.formatters[formatType]) != 'undefined') {
                            if (!angular.equals($scope.chart.formatters[formatType], $scope.oldChartFormatters[formatType])) {
                                $scope.oldChartFormatters[formatType] = $scope.chart.formatters[formatType];
                                $scope.formatters[formatType] = [];

                                if (formatType === 'color') {
                                    for (var cIdx = 0; cIdx < $scope.chart.formatters[formatType].length; cIdx++) {
                                        var colorFormat = new formatClass();

                                        for (i = 0; i < $scope.chart.formatters[formatType][cIdx].formats.length; i++) {
                                            var data = $scope.chart.formatters[formatType][cIdx].formats[i];

                                            if (typeof(data.fromBgColor) != 'undefined' && typeof(data.toBgColor) != 'undefined')
                                                colorFormat.addGradientRange(data.from, data.to, data.color, data.fromBgColor, data.toBgColor);
                                            else
                                                colorFormat.addRange(data.from, data.to, data.color, data.bgcolor);
                                        }

                                        $scope.formatters[formatType].push(colorFormat)
                                    }
                                } else {

                                    for (var i = 0; i < $scope.chart.formatters[formatType].length; i++) {
                                        $scope.formatters[formatType].push(new formatClass(
                                            $scope.chart.formatters[formatType][i])
                                        );
                                    }
                                }
                            }

                            //apply formats to dataTable
                            for (i = 0; i < $scope.formatters[formatType].length; i++) {
                                if ($scope.chart.formatters[formatType][i].columnNum < dataTable.getNumberOfColumns())
                                    $scope.formatters[formatType][i].format(dataTable, $scope.chart.formatters[formatType][i].columnNum);
                            }

                            //Many formatters require HTML tags to display special formatting
                            if (formatType === 'arrow' || formatType === 'bar' || formatType === 'color')
                                $scope.chart.options.allowHtml = true;
                        }
                    }

                    function draw() {
                        if (!draw.triggered && ($scope.chart != undefined)) {
                            draw.triggered = true;
                            $timeout(function () {

                                if (typeof ($scope.chartWrapper) == 'undefined') {
                                    var chartWrapperArgs = {
                                        chartType: $scope.chart.type,
                                        dataTable: $scope.chart.data,
                                        view: $scope.chart.view,
                                        options: $scope.chart.options,
                                        containerId: $elm[0]
                                    };

                                    $scope.chartWrapper = new google.visualization.ChartWrapper(chartWrapperArgs);
                                    google.visualization.events.addListener($scope.chartWrapper, 'ready', function () {
                                        $scope.chart.displayed = true;
                                        $scope.$apply(function (scope) {
                                            scope.onReady({ chartWrapper: scope.chartWrapper });
                                        });
                                    });
                                    google.visualization.events.addListener($scope.chartWrapper, 'error', function (err) {
                                        console.log("Chart not displayed due to error: " + err.message + ". Full error object follows.");
                                        console.log(err);
                                    });
                                    google.visualization.events.addListener($scope.chartWrapper, 'select', function () {
                                        var selectedItem = $scope.chartWrapper.getChart().getSelection()[0];
                                        $scope.$apply(function () {
                                            if ($attrs.select) {
                                                console.log('Angular-Google-Chart: The \'select\' attribute is deprecated and will be removed in a future release.  Please use \'onSelect\'.');
                                                $scope.select({ selectedItem: selectedItem });
                                            }
                                            else {
                                                $scope.onSelect({ selectedItem: selectedItem });
                                            }
                                        });
                                    });
                                }
                                else {
                                    $scope.chartWrapper.setChartType($scope.chart.type);
                                    $scope.chartWrapper.setDataTable($scope.chart.data);
                                    $scope.chartWrapper.setView($scope.chart.view);
                                    $scope.chartWrapper.setOptions($scope.chart.options);
                                }

                                if (typeof($scope.formatters) === 'undefined')
                                    $scope.formatters = {};

                                if (typeof($scope.chart.formatters) != 'undefined') {
                                    applyFormat("number", google.visualization.NumberFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("arrow", google.visualization.ArrowFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("date", google.visualization.DateFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("bar", google.visualization.BarFormat, $scope.chartWrapper.getDataTable());
                                    applyFormat("color", google.visualization.ColorFormat, $scope.chartWrapper.getDataTable());
                                }

                                var customFormatters = $scope.chart.customFormatters;
                                if (typeof(customFormatters) != 'undefined') {
                                    for (var name in customFormatters) {
                                        applyFormat(name, customFormatters[name], $scope.chartWrapper.getDataTable());
                                    }
                                }

                                $timeout(function () {
                                    $scope.chartWrapper.draw();
                                    draw.triggered = false;
                                });
                            }, 0, true);
                        }
                    }

                    function drawAsync() {
                        googleChartApiPromise.then(function () {
                            draw();
                        })
                    }
                }
            };
        }])

        .run(['$rootScope', '$window', function ($rootScope, $window) {
            angular.element($window).bind('resize', function () {
                $rootScope.$emit('resizeMsg');
            });
        }]);

})(document, window, window.angular);
<|endoftext|>


<commit_before>    /** @const */ var OAUTH_VERSION = '1.0';

    /**
     * OAuth
     *
     * @constructor
     */
    function OAuth(options) {
        if (!(this instanceof OAuth)) {
            return new OAuth(options);
        }

        return this.init(options);
    }

    OAuth.prototype = {
        init: function (options) {
            var oauth = {
                debug: options.debug || false,
                consumerKey: options.consumerKey,
                consumerSecret: options.consumerSecret,
                accessTokenSecret: options.accessTokenSecret || '',
                signatureMethod: options.signatureMethod || 'HMAC-SHA1'
            };

            /**
             * Makes an authenticated http request
             *
             * @param options {object}
             *      method {string} ['GET', 'POST', 'PUT', ...]
             *      url {string} A valid http(s) url
             *      data {object} A key value paired object of data
             *                      example: {'q':'foobar'}
             *                      for GET this will append a query string
             *      headers {object} A key value paired object of additional headers
             *      success {function} callback for a sucessful request
             *      failure {function} callback for a failed request
             */
            this.request = function (options) {
                var method, url, data, headers, success, failure, xhr, i,
                    headerParams, signatureMethod, signatureString, signature,
                    query = [];

                method = options.method || 'GET';
                url = options.url;
                data = options.data || {};
                headers = options.headers || {};
                success = options.success || function (data) {};
                failure = options.failure || function () {};

                if (oauth.debug) {
                    netscape.security.PrivilegeManager
                        .enablePrivilege("UniversalBrowserRead UniversalBrowserWrite");
                }

                xhr = Request(oauth.debug);
                xhr.onreadystatechange = function () {
                    if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
                        success({text: xhr.responseText, xml: xhr.responseXML});
                    } else if(xhr.readyState == 4 && xhr.status != 200 && xhr.status != 0) {
                        failure({text: xhr.responseText, xml: xhr.responseXML});
                    }
                };

                for(i in data) {
                    query.push(i + '=' + data[i]);
                }

                headerParams = {
                    'oauth_callback': 'oob',
                    'oauth_consumer_key': oauth.consumerKey,
                    'oauth_token': oauth.accessTokenSecret,
                    'oauth_signature_method': oauth.signatureMethod,
                    'oauth_timestamp': getTimestamp(),
                    'oauth_nonce': getNonce(),
                    'oauth_verifier': oauth.verifier || '',
                    'oauth_version': OAUTH_VERSION
                };

                signatureMethod = oauth.signatureMethod;

                signatureString = toSignatureBaseString(method, url, headerParams, query);
                signature = OAuth['signatureMethod'][signatureMethod](oauth.consumerSecret, oauth.accessTokenSecret, signatureString);

                headerParams.oauth_signature = signature;

                query = query.sort().join('&');
                if(method == 'GET') {
                    if (query) {
                        url += '?' + query;
                    }
                    query = null;
                } else {
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                }

                xhr.open(method, url, true);

                xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams));
                xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
                for (i in headers) {
                    xhr.setRequestHeader(i, headers[i]);
                }

                xhr.send(query);
            }

            return this;
        },

        authenticate: function (options) {},
        deauthenticate: function (options) {},

        request: '',

        /**
         * Wrapper for GET OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        get: function (url, success, failure) {
            this.request({'url': url, 'success': success, 'failure': failure});
        },

        /**
         * Wrapper for POST OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param data {object} A key value paired object of data
         *                      example: {'q':'foobar'}
         *                      for GET this will append a query string
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        post: function (url, data, success, failure) {
            this.request({'method': 'POST', 'url': url, 'data': data, 'success': success, 'failure': failure});
        },

        /**
         * Wrapper to parse a JSON string and pass it to the callback
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        getJSON: function (url, success, failure) {
            this.get(url, function (data) {
                success(JSON.parse(data.text));
            } , failure);
        },

        /**
         * Wrapper for JSON pasrsed GET OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        getXML: function (url, success, failure) {
            this.get(url, function (data) {
                success(data.xml);
            } , failure);
        }
    };

    OAuth.signatureMethod = {
        /**
         * Sign the request
         *
         * @param consumer_secret {string} the consumer secret
         * @param token_secret {string}  the token secret
         * @param signature_base {string}  the signature base string
         */
        'HMAC-SHA1': function (consumer_secret, token_secret, signature_base) {
            var passphrase, signature;

            consumer_secret = OAuth.urlEncode(consumer_secret);
            token_secret = OAuth.urlEncode(token_secret || '');

            passphrase = consumer_secret + '&' + token_secret;
            signature = HMAC(SHA1.prototype, passphrase, signature_base);

            return global.btoa(signature);
        }
    };

    /**
     * Get a string of the parameters for the OAuth Authorization header
     *
     * @param params {object} A key value paired object of data
     *                           example: {'q':'foobar'}
     *                           for GET this will append a query string
     */
    function toHeaderString(params) {
        var arr = [], i;

        for (i in params) {
            if (params[i] && params[i] != undefined && params[i] != '') {
                arr.push(i + '="' + OAuth.urlEncode(params[i]+'') + '"');
            }
        }

        return arr.sort().join(', ');
    }

    /**
     * Generate a signature base string for the request
     *
     * @param method {string} ['GET', 'POST', 'PUT', ...]
     * @param url {string} A valid http(s) url
     * @param header_params A key value paired object of additional headers
     * @param query_params {object} A key value paired object of data
     *                               example: {'q':'foobar'}
     *                               for GET this will append a query string
     */
    function toSignatureBaseString(method, url, header_parms, query_params) {
        var arr = [], i;

        for (i in header_parms) {
            if (header_parms[i] && header_parms[i] != undefined && header_parms[i] != '') {
                arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(header_parms[i]+''));
            }
        }

        for (i in query_params) {
            if (query_params[i] && query_params[i] != undefined && query_params[i] != '') {
                if (!oauth_header_params[i]) {
                    arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(query_params[i]+''));
                }
            }
        }

        return [
            method,
            OAuth.urlEncode(url),
            OAuth.urlEncode(arr.sort().join('&'))
        ].join('&');
    }

    /**
     * Generate a timestamp for the request
     */
    function getTimestamp() {
        return parseInt(+new Date / 1000, 10); // use short form of getting a timestamp
    };

    /**
     * Generate a nonce for the request
     *
     * @param key_length {number} Optional nonce length
     */
    function getNonce(key_length) {
        key_length = key_length || 64;

        var key_bytes = key_length / 8, value = '', key_iter = key_bytes / 4,
        key_remainder = key_bytes % 4, i,
        chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
                     '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
                     '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
                     '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
                     '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
                     '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
                     '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
                     '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
                     '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
                     '7A', '7B', '7C', '7D', '7E'];

        for (i = 0; i < key_iter; i++) {
            value += chars[rand()] + chars[rand()] + chars[rand()]+ chars[rand()];
        }

        // handle remaing bytes
        for (i = 0; i < key_remainder; i++) {
            value += chars[rand()];
        }

        function rand() {
            return Math.floor(Math.random() * chars.length);
        }

        return value;
    };

    /**
     * Url encode a string
     *
     * @param string {string} string to be url encoded
     */
    OAuth.urlEncode = function (string) {
        if (!string) return '';

        string = string + '';
        var reserved_chars = / |!|\*|"|'|\(|\)|;|:|@|&|=|\+|\$|,|\/|\?|%|#|\[|\]|<|>|{|}|\||\\|`|\^/,
            str_len = string.length, i, string_arr = string.split('');

        for (i = 0; i < str_len; i++) {
            if (string_arr[i].match(reserved_chars)) {
                string_arr[i] = '%' + (string_arr[i].charCodeAt(0)).toString(16).toUpperCase();
            }
        }

        return string_arr.join('');
    };

    /**
     * Url decode a string
     *
     * @param string {string} string to be url decoded
     */
    OAuth.urlDecode = function (string){
        if (!string) return '';

        return string.replace(/%[a-fA-F0-9]{2}/ig, function (match) {
            return String.fromCharCode(parseInt(match.replace('%', ''), 16));
        });
    };
<commit_msg>Remove XML related code as it breaks on Titanium<commit_after>    /** @const */ var OAUTH_VERSION = '1.0';

    /**
     * OAuth
     *
     * @constructor
     */
    function OAuth(options) {
        if (!(this instanceof OAuth)) {
            return new OAuth(options);
        }

        return this.init(options);
    }

    OAuth.prototype = {
        init: function (options) {
            var oauth = {
                debug: options.debug || false,
                consumerKey: options.consumerKey,
                consumerSecret: options.consumerSecret,
                accessTokenSecret: options.accessTokenSecret || '',
                signatureMethod: options.signatureMethod || 'HMAC-SHA1'
            };

            /**
             * Makes an authenticated http request
             *
             * @param options {object}
             *      method {string} ['GET', 'POST', 'PUT', ...]
             *      url {string} A valid http(s) url
             *      data {object} A key value paired object of data
             *                      example: {'q':'foobar'}
             *                      for GET this will append a query string
             *      headers {object} A key value paired object of additional headers
             *      success {function} callback for a sucessful request
             *      failure {function} callback for a failed request
             */
            this.request = function (options) {
                var method, url, data, headers, success, failure, xhr, i,
                    headerParams, signatureMethod, signatureString, signature,
                    query = [];

                method = options.method || 'GET';
                url = options.url;
                data = options.data || {};
                headers = options.headers || {};
                success = options.success || function (data) {};
                failure = options.failure || function () {};

                if (oauth.debug) {
                    netscape.security.PrivilegeManager
                        .enablePrivilege("UniversalBrowserRead UniversalBrowserWrite");
                }

                xhr = Request(oauth.debug);
                xhr.onreadystatechange = function () {
                    if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
                        success({text: xhr.responseText});//, xml: xhr.responseXML});
                    } else if(xhr.readyState == 4 && xhr.status != 200 && xhr.status != 0) {
                        failure({text: xhr.responseText});//, xml: xhr.responseXML});
                    }
                };

                for(i in data) {
                    query.push(i + '=' + data[i]);
                }

                headerParams = {
                    'oauth_callback': 'oob',
                    'oauth_consumer_key': oauth.consumerKey,
                    'oauth_token': oauth.accessTokenSecret,
                    'oauth_signature_method': oauth.signatureMethod,
                    'oauth_timestamp': getTimestamp(),
                    'oauth_nonce': getNonce(),
                    'oauth_verifier': oauth.verifier || '',
                    'oauth_version': OAUTH_VERSION
                };

                signatureMethod = oauth.signatureMethod;

                signatureString = toSignatureBaseString(method, url, headerParams, query);
                signature = OAuth['signatureMethod'][signatureMethod](oauth.consumerSecret, oauth.accessTokenSecret, signatureString);

                headerParams.oauth_signature = signature;

                query = query.sort().join('&');
                if(method == 'GET') {
                    if (query) {
                        url += '?' + query;
                    }
                    query = null;
                } else {
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                }

                xhr.open(method, url, true);

                xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams));
                xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
                for (i in headers) {
                    xhr.setRequestHeader(i, headers[i]);
                }

                xhr.send(query);
            }

            return this;
        },

        authenticate: function (options) {},
        deauthenticate: function (options) {},

        request: '',

        /**
         * Wrapper for GET OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        get: function (url, success, failure) {
            this.request({'url': url, 'success': success, 'failure': failure});
        },

        /**
         * Wrapper for POST OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param data {object} A key value paired object of data
         *                      example: {'q':'foobar'}
         *                      for GET this will append a query string
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        post: function (url, data, success, failure) {
            this.request({'method': 'POST', 'url': url, 'data': data, 'success': success, 'failure': failure});
        },

        /**
         * Wrapper to parse a JSON string and pass it to the callback
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        getJSON: function (url, success, failure) {
            this.get(url, function (data) {
                success(JSON.parse(data.text));
            } , failure);
        }

        /**
         * Wrapper for JSON pasrsed GET OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        /*getXML: function (url, success, failure) {
            this.get(url, function (data) {
                success(data.xml);
            } , failure);
        }*/
    };

    OAuth.signatureMethod = {
        /**
         * Sign the request
         *
         * @param consumer_secret {string} the consumer secret
         * @param token_secret {string}  the token secret
         * @param signature_base {string}  the signature base string
         */
        'HMAC-SHA1': function (consumer_secret, token_secret, signature_base) {
            var passphrase, signature;

            consumer_secret = OAuth.urlEncode(consumer_secret);
            token_secret = OAuth.urlEncode(token_secret || '');

            passphrase = consumer_secret + '&' + token_secret;
            signature = HMAC(SHA1.prototype, passphrase, signature_base);

            return global.btoa(signature);
        }
    };

    /**
     * Get a string of the parameters for the OAuth Authorization header
     *
     * @param params {object} A key value paired object of data
     *                           example: {'q':'foobar'}
     *                           for GET this will append a query string
     */
    function toHeaderString(params) {
        var arr = [], i;

        for (i in params) {
            if (params[i] && params[i] != undefined && params[i] != '') {
                arr.push(i + '="' + OAuth.urlEncode(params[i]+'') + '"');
            }
        }

        return arr.sort().join(', ');
    }

    /**
     * Generate a signature base string for the request
     *
     * @param method {string} ['GET', 'POST', 'PUT', ...]
     * @param url {string} A valid http(s) url
     * @param header_params A key value paired object of additional headers
     * @param query_params {object} A key value paired object of data
     *                               example: {'q':'foobar'}
     *                               for GET this will append a query string
     */
    function toSignatureBaseString(method, url, header_parms, query_params) {
        var arr = [], i;

        for (i in header_parms) {
            if (header_parms[i] && header_parms[i] != undefined && header_parms[i] != '') {
                arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(header_parms[i]+''));
            }
        }

        for (i in query_params) {
            if (query_params[i] && query_params[i] != undefined && query_params[i] != '') {
                if (!oauth_header_params[i]) {
                    arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(query_params[i]+''));
                }
            }
        }

        return [
            method,
            OAuth.urlEncode(url),
            OAuth.urlEncode(arr.sort().join('&'))
        ].join('&');
    }

    /**
     * Generate a timestamp for the request
     */
    function getTimestamp() {
        return parseInt(+new Date / 1000, 10); // use short form of getting a timestamp
    };

    /**
     * Generate a nonce for the request
     *
     * @param key_length {number} Optional nonce length
     */
    function getNonce(key_length) {
        key_length = key_length || 64;

        var key_bytes = key_length / 8, value = '', key_iter = key_bytes / 4,
        key_remainder = key_bytes % 4, i,
        chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
                     '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
                     '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
                     '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
                     '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
                     '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
                     '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
                     '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
                     '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
                     '7A', '7B', '7C', '7D', '7E'];

        for (i = 0; i < key_iter; i++) {
            value += chars[rand()] + chars[rand()] + chars[rand()]+ chars[rand()];
        }

        // handle remaing bytes
        for (i = 0; i < key_remainder; i++) {
            value += chars[rand()];
        }

        function rand() {
            return Math.floor(Math.random() * chars.length);
        }

        return value;
    };

    /**
     * Url encode a string
     *
     * @param string {string} string to be url encoded
     */
    OAuth.urlEncode = function (string) {
        if (!string) return '';

        string = string + '';
        var reserved_chars = / |!|\*|"|'|\(|\)|;|:|@|&|=|\+|\$|,|\/|\?|%|#|\[|\]|<|>|{|}|\||\\|`|\^/,
            str_len = string.length, i, string_arr = string.split('');

        for (i = 0; i < str_len; i++) {
            if (string_arr[i].match(reserved_chars)) {
                string_arr[i] = '%' + (string_arr[i].charCodeAt(0)).toString(16).toUpperCase();
            }
        }

        return string_arr.join('');
    };

    /**
     * Url decode a string
     *
     * @param string {string} string to be url decoded
     */
    OAuth.urlDecode = function (string){
        if (!string) return '';

        return string.replace(/%[a-fA-F0-9]{2}/ig, function (match) {
            return String.fromCharCode(parseInt(match.replace('%', ''), 16));
        });
    };
<|endoftext|>