<commit_before>
/*!
 * Jade - Lexer
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Initialize `Lexer` with the given `str`.
 *
 * Options:
 *
 *   - `colons` allow colons for attr delimiters
 *
 * @param {String} str
 * @param {Object} options
 * @api private
 */

var Lexer = module.exports = function Lexer(str, options) {
  options = options || {};
  this.input = str.replace(/\r\n|\r/g, '\n');
  this.colons = options.colons;
  this.deferredTokens = [];
  this.lastIndents = 0;
  this.lineno = 1;
  this.stash = [];
  this.indentStack = [];
  this.indentRe = null;
  this.pipeless = false;
};

/**
 * Lexer prototype.
 */

Lexer.prototype = {

  /**
   * Construct a token with the given `type` and `val`.
   *
   * @param {String} type
   * @param {String} val
   * @return {Object}
   * @api private
   */

  tok: function(type, val){
    return {
        type: type
      , line: this.lineno
      , val: val
    }
  },

  /**
   * Consume the given `len` of input.
   *
   * @param {Number} len
   * @api private
   */

  consume: function(len){
    this.input = this.input.substr(len);
  },

  /**
   * Scan for `type` with the given `regexp`.
   *
   * @param {String} type
   * @param {RegExp} regexp
   * @return {Object}
   * @api private
   */

  scan: function(regexp, type){
    var captures;
    if (captures = regexp.exec(this.input)) {
      this.consume(captures[0].length);
      return this.tok(type, captures[1]);
    }
  },

  /**
   * Defer the given `tok`.
   *
   * @param {Object} tok
   * @api private
   */

  defer: function(tok){
    this.deferredTokens.push(tok);
  },

  /**
   * Lookahead `n` tokens.
   *
   * @param {Number} n
   * @return {Object}
   * @api private
   */

  lookahead: function(n){
    var fetch = n - this.stash.length;
    while (fetch-- > 0) this.stash.push(this.next());
    return this.stash[--n];
  },

  /**
   * Return the indexOf `start` / `end` delimiters.
   *
   * @param {String} start
   * @param {String} end
   * @return {Number}
   * @api private
   */

  indexOfDelimiters: function(start, end){
    var str = this.input
      , nstart = 0
      , nend = 0
      , pos = 0;
    for (var i = 0, len = str.length; i < len; ++i) {
      if (start == str[i]) {
        ++nstart;
      } else if (end == str[i]) {
        if (++nend == nstart) {
          pos = i;
          break;
        }
      }
    }
    return pos;
  },

  /**
   * Stashed token.
   */

  stashed: function() {
    return this.stash.length
      && this.stash.shift();
  },

  /**
   * Deferred token.
   */

  deferred: function() {
    return this.deferredTokens.length
      && this.deferredTokens.shift();
  },

  /**
   * end-of-source.
   */

  eos: function() {
    if (this.input.length) return;
    if (this.indentStack.length) {
      this.indentStack.shift();
      return this.tok('outdent');
    } else {
      return this.tok('eos');
    }
  },

  /**
   * Comment.
   */

  comment: function() {
    var captures;
    if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok = this.tok('comment', captures[2]);
      tok.buffer = '-' != captures[1];
      return tok;
    }
  },

  /**
   * Tag.
   */

  tag: function() {
    var captures;
    if (captures = /^(\w[-:\w]*)/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok, name = captures[1];
      if (':' == name[name.length - 1]) {
        name = name.slice(0, -1);
        tok = this.tok('tag', name);
        this.defer(this.tok(':'));
        while (' ' == this.input[0]) this.input = this.input.substr(1);
      } else {
        tok = this.tok('tag', name);
      }
      return tok;
    }
  },

  /**
   * Filter.
   */

  filter: function() {
    return this.scan(/^:(\w+)/, 'filter');
  },

  /**
   * Doctype.
   */

  doctype: function() {
    return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype');
  },

  /**
   * Id.
   */

  id: function() {
    return this.scan(/^#([\w-]+)/, 'id');
  },

  /**
   * Class.
   */

  className: function() {
    return this.scan(/^\.([\w-]+)/, 'class');
  },

  /**
   * Text.
   */

  text: function() {
    return this.scan(/^(?:\| ?)?([^\n]+)/, 'text');
  },

  /**
   * Extends.
   */

  extends: function() {
    return this.scan(/^extends +([^\n]+)/, 'extends');
  },

  /**
   * Block.
   */

  block: function() {
    return this.scan(/^block +([^\n]+)/, 'block');
  },

  /**
   * Include.
   */

  include: function() {
    return this.scan(/^include +([^\n]+)/, 'include');
  },

  /**
   * Assignment.
   */

  assignment: function() {
    var captures;
    if (captures = /^(\w+) += *([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      var name = captures[1]
        , val = captures[2];
      return this.tok('code', 'var ' + name + ' = (' + val + ')');
    }
  },

  /**
   * Mixin.
   */

  mixin: function(){
    var captures;
    if (captures = /^mixin +([-\w]+)(?:\(([^\)]+)\))?/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok = this.tok('mixin', captures[1]);
      tok.args = captures[2];
      return tok;
    }
  },

  /**
   * Conditional.
   */

  conditional: function() {
    var captures;
    if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
      this.consume(captures[0].length);
      var type = captures[1]
        , js = captures[2];

      switch (type) {
        case 'if': js = 'if (' + js + ')'; break;
        case 'unless': js = 'if (!(' + js + '))'; break;
        case 'else if': js = 'else if (' + js + ')'; break;
        case 'else': js = 'else'; break;
      }

      return this.tok('code', js);
    }
  },

  /**
   * While.
   */

  while: function() {
    var captures;
    if (captures = /^while +([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      return this.tok('code', 'while (' + captures[1] + ')');
    }
  },

  /**
   * Each.
   */

  each: function() {
    var captures;
    if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok = this.tok('each', captures[1]);
      tok.key = captures[2] || '$index';
      tok.code = captures[3];
      return tok;
    }
  },

  /**
   * Code.
   */

  code: function() {
    var captures;
    if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      var flags = captures[1];
      captures[1] = captures[2];
      var tok = this.tok('code', captures[1]);
      tok.escape = flags[0] === '=';
      tok.buffer = flags[0] === '=' || flags[1] === '=';
      return tok;
    }
  },

  /**
   * Attributes.
   */

  attrs: function() {
    if ('(' == this.input[0]) {
      var index = this.indexOfDelimiters('(', ')')
        , str = this.input.substr(1, index-1)
        , tok = this.tok('attrs')
        , len = str.length
        , colons = this.colons
        , states = ['key']
        , key = ''
        , val = ''
        , quote
        , c;

      function state(){
        return states[states.length - 1];
      }

      function interpolate(attr) {
        return attr.replace(/#\{([^}]+)\}/g, function(_, expr){
          return quote + " + (" + expr + ") + " + quote;
        });
      }

      this.consume(index + 1);
      tok.attrs = {};

      function parse(c) {
        var real = c;
        // TODO: remove when people fix ":"
        if (colons && ':' == c) c = '=';
        // console.log('', c, state());
        switch (c) {
          case ',':
          case '\n':
            switch (state()) {
              case 'expr':
              case 'array':
              case 'string':
              case 'object':
                val += c;
                break;
              default:
                states.push('key');
                val = val.trim();
                key = key.trim();
                if ('' == key) return;
                tok.attrs[key.replace(/^['"]|['"]$/g, '')] = '' == val
                  ? true
                  : interpolate(val);
                key = val = '';
            }
            break;
          case '=':
            switch (state()) {
              case 'key char':
                key += real;
                break;
              case 'val':
              case 'expr':
              case 'array':
              case 'string':
              case 'object':
                val += real;
                break;
              default:
                states.push('val');
            }
            break;
          case '(':
            if ('val' == state()) states.push('expr');
            val += c;
            break;
          case ')':
            if ('expr' == state()) states.pop();
            val += c;
            break;
          case '{':
            if ('val' == state()) states.push('object');
            val += c;
            break;
          case '}':
            if ('object' == state()) states.pop();
            val += c;
            break;
          case '[':
            if ('val' == state()) states.push('array');
            val += c;
            break;
          case ']':
            if ('array' == state()) states.pop();
            val += c;
            break;
          case '"':
          case "'":
            switch (state()) {
              case 'key':
                states.push('key char');
                break;
              case 'key char':
                states.pop();
                break;
              case 'string':
                if (c == quote) states.pop();
                val += c;
                break;
              default:
                states.push('string');
                val += c;
                quote = c;
            }
            break;
          case '':
            break;
          default:
            switch (state()) {
              case 'key':
              case 'key char':
                key += c;
                break;
              default:
                val += c;
            }
        }
      }

      for (var i = 0; i < len; ++i) {
        parse(str[i]);
      }

      parse(',');

      return tok;
    }
  },

  /**
   * Indent | Outdent | Newline.
   */

  indent: function() {
    var captures, re;

    // established regexp
    if (this.indentRe) {
      captures = this.indentRe.exec(this.input);
    // determine regexp
    } else {
      // tabs
      re = /^\n(\t*) */;
      captures = re.exec(this.input);

      // spaces
      if (captures && !captures[1].length) {
        re = /^\n( *)/;
        captures = re.exec(this.input);
      }

      // established
      if (captures && captures[1].length) this.indentRe = re;
    }

    if (captures) {
      var tok
        , indents = captures[1].length;

      ++this.lineno;
      this.consume(indents + 1);

      if (' ' == this.input[0] || '\t' == this.input[0]) {
        throw new Error('Invalid indentation, you can use tabs or spaces but not both');
      }

      // blank line
      if ('\n' == this.input[0]) return this.tok('newline');

      // outdent
      if (this.indentStack.length && indents < this.indentStack[0]) {
        while (this.indentStack.length && this.indentStack[0] > indents) {
          this.stash.push(this.tok('outdent'));
          this.indentStack.shift();
        }
        tok = this.stash.pop();
      // indent
      } else if (indents && indents != this.indentStack[0]) {
        this.indentStack.unshift(indents);
        tok = this.tok('indent', indents);
      // newline
      } else {
        tok = this.tok('newline');
      }

      return tok;
    }
  },

  /**
   * Pipe-less text consumed only when
   * pipeless is true;
   */

  pipelessText: function() {
    if (this.pipeless) {
      if ('\n' == this.input[0]) return;
      var i = this.input.indexOf('\n');
      if (-1 == i) i = this.input.length;
      var str = this.input.substr(0, i);
      this.consume(str.length);
      return this.tok('text', str);
    }
  },

  /**
   * ':'
   */

  colon: function() {
    return this.scan(/^: */, ':');
  },

  /**
   * Return the next token object, or those
   * previously stashed by lookahead.
   *
   * @return {Object}
   * @api private
   */

  advance: function(){
    return this.stashed()
      || this.next();
  },

  /**
   * Return the next token object.
   *
   * @return {Object}
   * @api private
   */

  next: function() {
    return this.deferred()
      || this.eos()
      || this.pipelessText()
      || this.doctype()
      || this.extends()
      || this.block()
      || this.include()
      || this.mixin()
      || this.conditional()
      || this.each()
      || this.while()
      || this.assignment()
      || this.tag()
      || this.filter()
      || this.code()
      || this.id()
      || this.className()
      || this.attrs()
      || this.indent()
      || this.comment()
      || this.colon()
      || this.text();
  }
};
<commit_msg>Fixed attr state issue. Closes #353<commit_after>
/*!
 * Jade - Lexer
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Initialize `Lexer` with the given `str`.
 *
 * Options:
 *
 *   - `colons` allow colons for attr delimiters
 *
 * @param {String} str
 * @param {Object} options
 * @api private
 */

var Lexer = module.exports = function Lexer(str, options) {
  options = options || {};
  this.input = str.replace(/\r\n|\r/g, '\n');
  this.colons = options.colons;
  this.deferredTokens = [];
  this.lastIndents = 0;
  this.lineno = 1;
  this.stash = [];
  this.indentStack = [];
  this.indentRe = null;
  this.pipeless = false;
};

/**
 * Lexer prototype.
 */

Lexer.prototype = {

  /**
   * Construct a token with the given `type` and `val`.
   *
   * @param {String} type
   * @param {String} val
   * @return {Object}
   * @api private
   */

  tok: function(type, val){
    return {
        type: type
      , line: this.lineno
      , val: val
    }
  },

  /**
   * Consume the given `len` of input.
   *
   * @param {Number} len
   * @api private
   */

  consume: function(len){
    this.input = this.input.substr(len);
  },

  /**
   * Scan for `type` with the given `regexp`.
   *
   * @param {String} type
   * @param {RegExp} regexp
   * @return {Object}
   * @api private
   */

  scan: function(regexp, type){
    var captures;
    if (captures = regexp.exec(this.input)) {
      this.consume(captures[0].length);
      return this.tok(type, captures[1]);
    }
  },

  /**
   * Defer the given `tok`.
   *
   * @param {Object} tok
   * @api private
   */

  defer: function(tok){
    this.deferredTokens.push(tok);
  },

  /**
   * Lookahead `n` tokens.
   *
   * @param {Number} n
   * @return {Object}
   * @api private
   */

  lookahead: function(n){
    var fetch = n - this.stash.length;
    while (fetch-- > 0) this.stash.push(this.next());
    return this.stash[--n];
  },

  /**
   * Return the indexOf `start` / `end` delimiters.
   *
   * @param {String} start
   * @param {String} end
   * @return {Number}
   * @api private
   */

  indexOfDelimiters: function(start, end){
    var str = this.input
      , nstart = 0
      , nend = 0
      , pos = 0;
    for (var i = 0, len = str.length; i < len; ++i) {
      if (start == str[i]) {
        ++nstart;
      } else if (end == str[i]) {
        if (++nend == nstart) {
          pos = i;
          break;
        }
      }
    }
    return pos;
  },

  /**
   * Stashed token.
   */

  stashed: function() {
    return this.stash.length
      && this.stash.shift();
  },

  /**
   * Deferred token.
   */

  deferred: function() {
    return this.deferredTokens.length
      && this.deferredTokens.shift();
  },

  /**
   * end-of-source.
   */

  eos: function() {
    if (this.input.length) return;
    if (this.indentStack.length) {
      this.indentStack.shift();
      return this.tok('outdent');
    } else {
      return this.tok('eos');
    }
  },

  /**
   * Comment.
   */

  comment: function() {
    var captures;
    if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok = this.tok('comment', captures[2]);
      tok.buffer = '-' != captures[1];
      return tok;
    }
  },

  /**
   * Tag.
   */

  tag: function() {
    var captures;
    if (captures = /^(\w[-:\w]*)/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok, name = captures[1];
      if (':' == name[name.length - 1]) {
        name = name.slice(0, -1);
        tok = this.tok('tag', name);
        this.defer(this.tok(':'));
        while (' ' == this.input[0]) this.input = this.input.substr(1);
      } else {
        tok = this.tok('tag', name);
      }
      return tok;
    }
  },

  /**
   * Filter.
   */

  filter: function() {
    return this.scan(/^:(\w+)/, 'filter');
  },

  /**
   * Doctype.
   */

  doctype: function() {
    return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype');
  },

  /**
   * Id.
   */

  id: function() {
    return this.scan(/^#([\w-]+)/, 'id');
  },

  /**
   * Class.
   */

  className: function() {
    return this.scan(/^\.([\w-]+)/, 'class');
  },

  /**
   * Text.
   */

  text: function() {
    return this.scan(/^(?:\| ?)?([^\n]+)/, 'text');
  },

  /**
   * Extends.
   */

  extends: function() {
    return this.scan(/^extends +([^\n]+)/, 'extends');
  },

  /**
   * Block.
   */

  block: function() {
    return this.scan(/^block +([^\n]+)/, 'block');
  },

  /**
   * Include.
   */

  include: function() {
    return this.scan(/^include +([^\n]+)/, 'include');
  },

  /**
   * Assignment.
   */

  assignment: function() {
    var captures;
    if (captures = /^(\w+) += *([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      var name = captures[1]
        , val = captures[2];
      return this.tok('code', 'var ' + name + ' = (' + val + ')');
    }
  },

  /**
   * Mixin.
   */

  mixin: function(){
    var captures;
    if (captures = /^mixin +([-\w]+)(?:\(([^\)]+)\))?/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok = this.tok('mixin', captures[1]);
      tok.args = captures[2];
      return tok;
    }
  },

  /**
   * Conditional.
   */

  conditional: function() {
    var captures;
    if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
      this.consume(captures[0].length);
      var type = captures[1]
        , js = captures[2];

      switch (type) {
        case 'if': js = 'if (' + js + ')'; break;
        case 'unless': js = 'if (!(' + js + '))'; break;
        case 'else if': js = 'else if (' + js + ')'; break;
        case 'else': js = 'else'; break;
      }

      return this.tok('code', js);
    }
  },

  /**
   * While.
   */

  while: function() {
    var captures;
    if (captures = /^while +([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      return this.tok('code', 'while (' + captures[1] + ')');
    }
  },

  /**
   * Each.
   */

  each: function() {
    var captures;
    if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      var tok = this.tok('each', captures[1]);
      tok.key = captures[2] || '$index';
      tok.code = captures[3];
      return tok;
    }
  },

  /**
   * Code.
   */

  code: function() {
    var captures;
    if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
      this.consume(captures[0].length);
      var flags = captures[1];
      captures[1] = captures[2];
      var tok = this.tok('code', captures[1]);
      tok.escape = flags[0] === '=';
      tok.buffer = flags[0] === '=' || flags[1] === '=';
      return tok;
    }
  },

  /**
   * Attributes.
   */

  attrs: function() {
    if ('(' == this.input[0]) {
      var index = this.indexOfDelimiters('(', ')')
        , str = this.input.substr(1, index-1)
        , tok = this.tok('attrs')
        , len = str.length
        , colons = this.colons
        , states = ['key']
        , key = ''
        , val = ''
        , quote
        , c;

      function state(){
        return states[states.length - 1];
      }

      function interpolate(attr) {
        return attr.replace(/#\{([^}]+)\}/g, function(_, expr){
          return quote + " + (" + expr + ") + " + quote;
        });
      }

      this.consume(index + 1);
      tok.attrs = {};

      function parse(c) {
        var real = c;
        // TODO: remove when people fix ":"
        if (colons && ':' == c) c = '=';
        switch (c) {
          case ',':
          case '\n':
            switch (state()) {
              case 'expr':
              case 'array':
              case 'string':
              case 'object':
                val += c;
                break;
              default:
                states.push('key');
                val = val.trim();
                key = key.trim();
                if ('' == key) return;
                tok.attrs[key.replace(/^['"]|['"]$/g, '')] = '' == val
                  ? true
                  : interpolate(val);
                key = val = '';
            }
            break;
          case '=':
            switch (state()) {
              case 'key char':
                key += real;
                break;
              case 'val':
              case 'expr':
              case 'array':
              case 'string':
              case 'object':
                val += real;
                break;
              default:
                states.push('val');
            }
            break;
          case '(':
            if ('val' == state()
              || 'expr' == state()) states.push('expr');
            val += c;
            break;
          case ')':
            if ('expr' == state()
              || 'val' == state()) states.pop();
            val += c;
            break;
          case '{':
            if ('val' == state()) states.push('object');
            val += c;
            break;
          case '}':
            if ('object' == state()) states.pop();
            val += c;
            break;
          case '[':
            if ('val' == state()) states.push('array');
            val += c;
            break;
          case ']':
            if ('array' == state()) states.pop();
            val += c;
            break;
          case '"':
          case "'":
            switch (state()) {
              case 'key':
                states.push('key char');
                break;
              case 'key char':
                states.pop();
                break;
              case 'string':
                if (c == quote) states.pop();
                val += c;
                break;
              default:
                states.push('string');
                val += c;
                quote = c;
            }
            break;
          case '':
            break;
          default:
            switch (state()) {
              case 'key':
              case 'key char':
                key += c;
                break;
              default:
                val += c;
            }
        }
      }

      for (var i = 0; i < len; ++i) {
        parse(str[i]);
      }

      parse(',');

      return tok;
    }
  },

  /**
   * Indent | Outdent | Newline.
   */

  indent: function() {
    var captures, re;

    // established regexp
    if (this.indentRe) {
      captures = this.indentRe.exec(this.input);
    // determine regexp
    } else {
      // tabs
      re = /^\n(\t*) */;
      captures = re.exec(this.input);

      // spaces
      if (captures && !captures[1].length) {
        re = /^\n( *)/;
        captures = re.exec(this.input);
      }

      // established
      if (captures && captures[1].length) this.indentRe = re;
    }

    if (captures) {
      var tok
        , indents = captures[1].length;

      ++this.lineno;
      this.consume(indents + 1);

      if (' ' == this.input[0] || '\t' == this.input[0]) {
        throw new Error('Invalid indentation, you can use tabs or spaces but not both');
      }

      // blank line
      if ('\n' == this.input[0]) return this.tok('newline');

      // outdent
      if (this.indentStack.length && indents < this.indentStack[0]) {
        while (this.indentStack.length && this.indentStack[0] > indents) {
          this.stash.push(this.tok('outdent'));
          this.indentStack.shift();
        }
        tok = this.stash.pop();
      // indent
      } else if (indents && indents != this.indentStack[0]) {
        this.indentStack.unshift(indents);
        tok = this.tok('indent', indents);
      // newline
      } else {
        tok = this.tok('newline');
      }

      return tok;
    }
  },

  /**
   * Pipe-less text consumed only when
   * pipeless is true;
   */

  pipelessText: function() {
    if (this.pipeless) {
      if ('\n' == this.input[0]) return;
      var i = this.input.indexOf('\n');
      if (-1 == i) i = this.input.length;
      var str = this.input.substr(0, i);
      this.consume(str.length);
      return this.tok('text', str);
    }
  },

  /**
   * ':'
   */

  colon: function() {
    return this.scan(/^: */, ':');
  },

  /**
   * Return the next token object, or those
   * previously stashed by lookahead.
   *
   * @return {Object}
   * @api private
   */

  advance: function(){
    return this.stashed()
      || this.next();
  },

  /**
   * Return the next token object.
   *
   * @return {Object}
   * @api private
   */

  next: function() {
    return this.deferred()
      || this.eos()
      || this.pipelessText()
      || this.doctype()
      || this.extends()
      || this.block()
      || this.include()
      || this.mixin()
      || this.conditional()
      || this.each()
      || this.while()
      || this.assignment()
      || this.tag()
      || this.filter()
      || this.code()
      || this.id()
      || this.className()
      || this.attrs()
      || this.indent()
      || this.comment()
      || this.colon()
      || this.text();
  }
};
<|endoftext|>


<commit_before>const childProcess = require('child_process')

module.exports = {
  build: [
    `types-as-schema src/types.ts --json static/ --protobuf static/protocol.proto`,
    {
      back: [
        `file2variable-cli src/sql/*.sql -o src/variables.ts --base src/sql`,
        `rimraf dist/`,
        `tsc -p src`
      ],
      front: {
        js: [
          `file2variable-cli static/*.template.html static/protocol.proto static/request-protocol.json static/response-protocol.json -o static/variables.ts --html-minify --json --protobuf --base static`,
          `tsc -p static`,
          `webpack --display-modules --config static/webpack.config.js`
        ],
        css: {
          vendor: `cleancss ./node_modules/github-fork-ribbon-css/gh-fork-ribbon.css ./node_modules/tab-container-component/tab-container.min.css -o ./static/vendor.bundle.css`,
          index: [
            `lessc ./static/index.less > ./static/index.css`,
            `cleancss ./static/index.css -o ./static/index.bundle.css`
          ]
        },
        clean: `rimraf static/*.bundle-*.js static/vendor.bundle-*.css static/index.bundle-*.css`
      }
    },
    `rev-static --config static/rev-static.config.js`,
    async () => {
      const puppeteer = require('puppeteer')
      const server = childProcess.exec('node ./dist/index.js')
      server.stdout.pipe(process.stdout)
      server.stderr.pipe(process.stderr)
      const browser = await puppeteer.launch()
      const page = await browser.newPage()
      await page.waitFor(1000)
      await page.goto(`http://localhost:9000`)
      await page.waitFor(1000)
      await page.screenshot({ path: `static/screenshot.png`, fullPage: true })
      server.kill()
      browser.close()
    }
  ],
  lint: {
    ts: `tslint "src/**/*.ts" "static/**/*.ts"`,
    js: `standard "**/*.config.js"`,
    less: `stylelint "online/**/*.less"`,
    export: `no-unused-export "src/**/*.ts" "static/**/*.ts"`
  },
  test: {
    jasmine: [
      'tsc -p spec',
      'jasmine',
      'git checkout static/screenshot.png',
      () => new Promise((resolve, reject) => {
        childProcess.exec('git status -s', (error, stdout, stderr) => {
          if (error) {
            reject(error)
          } else {
            if (stdout) {
              reject(new Error(`generated files doesn't match.`))
            } else {
              resolve()
            }
          }
        }).stdout.pipe(process.stdout)
      })
    ],
    karma: [
      'tsc -p static_spec',
      process.env.APPVEYOR ? 'echo "skip karma test"' : 'karma start static_spec/karma.config.js'
    ]
  },
  fix: {
    ts: `tslint --fix "src/**/*.ts" "static/**/*.ts"`,
    js: `standard --fix "**/*.config.js"`,
    less: `stylelint --fix "online/**/*.less"`
  },
  release: `clean-release`,
  watch: {
    schema: `watch-then-execute "src/types.ts" --script "clean-scripts build[0]"`,
    sql: `file2variable-cli src/sql/*.sql -o src/variables.ts --base src/sql --watch`,
    back: `tsc -p src --watch`,
    template: `file2variable-cli static/*.template.html static/protocol.proto static/request-protocol.json static/response-protocol.json -o static/variables.ts --html-minify --json --protobuf --base static --watch`,
    front: `tsc -p static --watch`,
    webpack: `webpack --config static/webpack.config.js --watch`,
    less: `watch-then-execute "./static/index.less" --script "clean-scripts build[1].front.css.index"`,
    rev: `rev-static --config static/rev-static.config.js --watch`
  },
  prerender: [
    async () => {
      const puppeteer = require('puppeteer')
      const fs = require('fs')
      const server = childProcess.exec('node ./dist/index.js')
      server.stdout.pipe(process.stdout)
      server.stderr.pipe(process.stderr)
      const browser = await puppeteer.launch()
      const page = await browser.newPage()
      await page.waitFor(1000)
      await page.goto('http://localhost:9000')
      await page.waitFor(1000)
      const content = await page.evaluate(() => {
        const element = document.querySelector('#prerender-container')
        return element ? element.innerHTML : ''
      })
      fs.writeFileSync('static/prerender.html', content)
      server.kill()
      browser.close()
    },
    `clean-scripts build[2]`
  ]
}
<commit_msg>try to fix CI<commit_after>const childProcess = require('child_process')

module.exports = {
  build: [
    `types-as-schema src/types.ts --json static/ --protobuf static/protocol.proto`,
    {
      back: [
        `file2variable-cli src/sql/*.sql -o src/variables.ts --base src/sql`,
        `rimraf dist/`,
        `tsc -p src`
      ],
      front: {
        js: [
          `file2variable-cli static/*.template.html static/protocol.proto static/request-protocol.json static/response-protocol.json -o static/variables.ts --html-minify --json --protobuf --base static`,
          `tsc -p static`,
          `webpack --display-modules --config static/webpack.config.js`
        ],
        css: {
          vendor: `cleancss ./node_modules/github-fork-ribbon-css/gh-fork-ribbon.css ./node_modules/tab-container-component/tab-container.min.css -o ./static/vendor.bundle.css`,
          index: [
            `lessc ./static/index.less > ./static/index.css`,
            `cleancss ./static/index.css -o ./static/index.bundle.css`
          ]
        },
        clean: `rimraf static/*.bundle-*.js static/vendor.bundle-*.css static/index.bundle-*.css`
      }
    },
    `rev-static --config static/rev-static.config.js`,
    async () => {
      const puppeteer = require('puppeteer')
      const server = childProcess.exec('node ./dist/index.js')
      server.stdout.pipe(process.stdout)
      server.stderr.pipe(process.stderr)
      const browser = await puppeteer.launch()
      const page = await browser.newPage()
      await page.waitFor(1000)
      await page.goto(`http://localhost:9000`)
      await page.waitFor(1000)
      await page.screenshot({ path: `static/screenshot.png`, fullPage: true })
      server.kill('SIGINT')
      browser.close()
    }
  ],
  lint: {
    ts: `tslint "src/**/*.ts" "static/**/*.ts"`,
    js: `standard "**/*.config.js"`,
    less: `stylelint "online/**/*.less"`,
    export: `no-unused-export "src/**/*.ts" "static/**/*.ts"`
  },
  test: {
    jasmine: [
      'tsc -p spec',
      'jasmine',
      'git checkout static/screenshot.png',
      () => new Promise((resolve, reject) => {
        childProcess.exec('git status -s', (error, stdout, stderr) => {
          if (error) {
            reject(error)
          } else {
            if (stdout) {
              reject(new Error(`generated files doesn't match.`))
            } else {
              resolve()
            }
          }
        }).stdout.pipe(process.stdout)
      })
    ],
    karma: [
      'tsc -p static_spec',
      process.env.APPVEYOR ? 'echo "skip karma test"' : 'karma start static_spec/karma.config.js'
    ]
  },
  fix: {
    ts: `tslint --fix "src/**/*.ts" "static/**/*.ts"`,
    js: `standard --fix "**/*.config.js"`,
    less: `stylelint --fix "online/**/*.less"`
  },
  release: `clean-release`,
  watch: {
    schema: `watch-then-execute "src/types.ts" --script "clean-scripts build[0]"`,
    sql: `file2variable-cli src/sql/*.sql -o src/variables.ts --base src/sql --watch`,
    back: `tsc -p src --watch`,
    template: `file2variable-cli static/*.template.html static/protocol.proto static/request-protocol.json static/response-protocol.json -o static/variables.ts --html-minify --json --protobuf --base static --watch`,
    front: `tsc -p static --watch`,
    webpack: `webpack --config static/webpack.config.js --watch`,
    less: `watch-then-execute "./static/index.less" --script "clean-scripts build[1].front.css.index"`,
    rev: `rev-static --config static/rev-static.config.js --watch`
  },
  prerender: [
    async () => {
      const puppeteer = require('puppeteer')
      const fs = require('fs')
      const server = childProcess.exec('node ./dist/index.js')
      server.stdout.pipe(process.stdout)
      server.stderr.pipe(process.stderr)
      const browser = await puppeteer.launch()
      const page = await browser.newPage()
      await page.waitFor(1000)
      await page.goto('http://localhost:9000')
      await page.waitFor(1000)
      const content = await page.evaluate(() => {
        const element = document.querySelector('#prerender-container')
        return element ? element.innerHTML : ''
      })
      fs.writeFileSync('static/prerender.html', content)
      server.kill()
      browser.close()
    },
    `clean-scripts build[2]`
  ]
}
<|endoftext|>


<commit_before>/**
 * Shopware 5
 * Copyright (c) shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */

//{namespace name=backend/index/view/menu}

Ext.define('Shopware.notification.SubscriptionWarning', {

    snippets: {
        licence_upgrade_warning: '[0]x plugin(s) require a licence upgrade.

Required upgrades:
[1]',
        subscription_warning: 'Subscription(s) for [0]x plugin(s) are expired.

Expired plugins:
[1]',
        expired_soon_subscription_warning: 'Subscription(s) for [0]x plugin(s) expire in a few days.

Soon expired plugins:
[1]',
        expired_soon_subscription_days_warning: ' days',
        invalid_licence: 'Licence(s) of [0]x plugin(s) are invalid.

Invalid licence(s):
[1]',
        shop_license_upgrade : 'The license upgrade for the shop hasn\'t been executed yet.',
        no_license: 'You may be a victim of counterfeiting.

No valid license found for plugins:
[1]',
        expiring_license: 'Expiring license(s)',
        expired_license: 'Expired license(s)',
        expiring_license_warning: 'License(s) of [0]x plugin(s) are soon expiring.

Soon expired license(s):
[1]',
        expired_license_warning: 'At least one license of your used plugins has expired.
Check this in your Shopware account under "Licenses > Licenses" and update your license immediately.',
        unknown_license: 'Unlicensed plugins',
        confirm_open_pluginmanager: 'You have installed unlicensed plugins. Do you want to open the Plugin Manager now to check your plugins?',
        subscription: 'Subscription',
        subscription_hide_message: 'Would you like to hide this message for a week?',
        openPluginOverview: 'Open plugin overview',
        importantInformation: 'Important Information',
        noShopSecretWarning: 'In order to receive information about updates and install plugins, you need to log in to your Shopware account. If you don\'t have a Shopware account yet, you can easily register.',
        login: 'Login now'
    },

    /**
     * Check if any plugins need to be upgraded
     */
    check: function () {
        var me = this;

        me.getPluginInformation(function (data) {
            var pluginData = me.preparePluginData(data);
            pluginData.expiredPluginSubscriptions.sort(me.sortPluginsByDaysLeftCallback);
            me.displayNotices(pluginData, data);
        });
    },

    checkSecret: function () {
        Ext.Ajax.request({
            url: '{url controller="PluginManager" action="checkSecret"}'
        });
    },

    preparePluginData: function(data) {
        var preparedData = {
                isShopUpgraded: data.isShopUpgraded,
                notUpgradedPlugins: [],
                wrongVersionPlugins: [],
                expiredPluginSubscriptions: [],
                unknownLicensePlugins: [],
                expiredLicensePlugins: []
            },
            plugins = data.plugins,
            today = Ext.Date.clearTime(new Date()),
            i = 0,
            count = plugins.length;

        for (i; i < count; i++) {
            var plugin = plugins[i];
            if (plugin.subscriptionUpgradeRequired) {
                preparedData.notUpgradedPlugins.push({
                    label: plugin.label
                });
            }
            if (plugin.wrongSubscription) {
                preparedData.wrongVersionPlugins.push({
                    label: plugin.label
                });
            }
            if (!Ext.isEmpty(plugin.subscriptionExpiration)) {
                var subscriptionExpirationDate = Ext.Date.parse(plugin.subscriptionExpiration, 'Y-m-d'),
                    isSubscriptionExpired, daysDiffSubscription;

                if (!Ext.isEmpty(subscriptionExpirationDate)) {
                    isSubscriptionExpired = subscriptionExpirationDate < today;
                    daysDiffSubscription = Math.round(Math.abs((subscriptionExpirationDate.getTime() - today.getTime())/(1000 * 60 * 60 * 24)));

                    if (isSubscriptionExpired) {
                        preparedData.expiredPluginSubscriptions.push({
                            label: plugin.label,
                            expired: isSubscriptionExpired,
                            daysLeft: isSubscriptionExpired ? 0 : daysDiffSubscription,
                            dayDiff: daysDiffSubscription
                        });
                    }
                }
            }
            if (plugin.unknownLicense) {
                preparedData.unknownLicensePlugins.push({
                    label: plugin.label
                });
            }
            if (!Ext.isEmpty(plugin.licenseExpiration)) {
                var licenseExpiration = Ext.Date.parse(plugin.licenseExpiration, 'Y-m-d'),
                    isLicenseExpired, daysDiffLicense;

                if (!Ext.isEmpty(licenseExpiration)) {
                    isLicenseExpired = licenseExpiration < today;
                    daysDiffLicense = Math.round(Math.abs((licenseExpiration.getTime() - today.getTime())/(1000 * 60 * 60 * 24)));

                    if (!isLicenseExpired && daysDiffLicense > 14) {
                        continue;
                    }

                    preparedData.expiredLicensePlugins.push({
                        label: plugin.label,
                        expired: isLicenseExpired,
                        daysLeft: isLicenseExpired ? 0 : daysDiffLicense,
                        dayDiff: daysDiffLicense
                    });
                }
            }
        }

        return preparedData;
    },

    getPluginInformation: function (callback) {
        var me = this;

        Ext.Ajax.request({
            url: '{url controller="PluginManager" action="getPluginInformation"}',
            success: function (response) {
                var responseData = Ext.decode(response.responseText);

                if (!responseData || Ext.isEmpty(responseData.data)) {
                    return;
                }

                if (responseData.data.shopSecretMissing) {
                    Shopware.Notification.createStickyGrowlMessage({
                        title: me.snippets.importantInformation,
                        text: me.snippets.noShopSecretWarning,
                        width: 330,
                        btnDetail: {
                            text: me.snippets.login,
                            callback: function () {
                                Shopware.app.Application.addSubApplication({
                                    name: 'Shopware.apps.PluginManager',
                                    params: {
                                        openLogin: true
                                    }
                                });
                            }
                        }
                    });
                }

                if (responseData.success === true && responseData.data) {
                    callback(responseData.data);
                }
            }
        });
    },

    displayNotices: function(data, rawData) {
        var me = this,
            expiredLicensePlugins = me.filterExpiredPlugins(data.expiredLicensePlugins, true),
            soonExpiredLicensePlugins = me.filterExpiredPlugins(data.expiredLicensePlugins, false),
            expiredPlugins = me.filterExpiredPlugins(data.expiredPluginSubscriptions, true),
            soonExpiredPlugins = me.filterExpiredPlugins(data.expiredPluginSubscriptions, false);

        if(data.isShopUpgraded == false) {
            me.displayShopNotUpgradedShopMessage();
        }

        if (data.notUpgradedPlugins && data.notUpgradedPlugins.length > 0) {
            me.displayNotUpgradedNotice(data.notUpgradedPlugins);
        }

        if (data.wrongVersionPlugins && data.wrongVersionPlugins.length > 0) {
            me.displayWrongVersionNotice(data.wrongVersionPlugins);
        }

        if (expiredPlugins && expiredPlugins.length > 0) {
            me.displaySubscriptionNotice(expiredPlugins);
        }

        if (soonExpiredPlugins && soonExpiredPlugins.length > 0) {
            me.displayExpiredSoonSubscriptionNotice(soonExpiredPlugins);
        }

        if (expiredLicensePlugins && expiredLicensePlugins.length > 0) {
            if (!rawData.live) {
                return;
            }

            switch (me.getExpiredMode(expiredLicensePlugins)) {
                case 'stop':
                    Ext.create('Shopware.window.ExpiredPluginStop');
                    break;
                case 'warning':
                    Ext.create('Shopware.window.ExpiredPluginWarning');
                    break;

                default:
                    me.displayExpiredLicensePluginsNotice(expiredLicensePlugins);
            }
        }

        if (soonExpiredLicensePlugins && soonExpiredLicensePlugins.length > 0) {
            me.displaySoonExpiredLicensePluginsNotice(soonExpiredLicensePlugins);
        }

        if (data.unknownLicensePlugins && data.unknownLicensePlugins.length > 0) {
            me.displayUnknownLicensePluginsNotice(data.unknownLicensePlugins);
        }
    },

    displayWrongVersionNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.invalid_licence, plugins.length, pluginNames),
            width: 440
        });
    },

    displayNotUpgradedNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.licence_upgrade_warning, plugins.length, pluginNames),
            width: 440
        });

    },

    displaySubscriptionNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        if (Ext.util.Cookies.get('hideSubscriptionNotice') !== null) {
            return;
        }

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.subscription_warning, plugins.length, pluginNames),
            width: 440,
            onCloseButton: function() {
                Ext.Msg.confirm(
                    me.snippets.subscription,
                    me.snippets.subscription_hide_message,
                    function(answer) {
                        if (answer === 'yes') {
                            var currentDate = new Date();
                            currentDate.setDate(currentDate.getDate() + 7);
                            Ext.util.Cookies.set('hideSubscriptionNotice', 1, currentDate);
                        }
                    }
                );
            }
        });
    },

    displayExpiredSoonSubscriptionNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getSoonExpiredPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.expired_soon_subscription_warning, plugins.length, pluginNames),
            width: 440
        });
    },

    displayShopNotUpgradedShopMessage: function(data) {
        var me = this;
        Shopware.Notification.createStickyGrowlMessage({
            text: '' + me.snippets.shop_license_upgrade + '',
            width: 440
        });
    },

    displayExpiredLicensePluginsNotice: function() {
        var me = this;

        Shopware.Notification.createStickyGrowlMessage({
            title: me.snippets.expired_license,
            text: me.snippets.expired_license_warning,
            width: 440,
            btnDetail: {
                text: me.snippets.openPluginOverview,
                callback: function () {
                    Shopware.app.Application.addSubApplication({
                        name: 'Shopware.apps.PluginManager',
                        action: 'ExpiredPlugins'
                    });
                }
            }
        });
    },

    displaySoonExpiredLicensePluginsNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getSoonExpiredPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            title: me.snippets.expiring_license,
            text: Ext.String.format(me.snippets.expiring_license_warning, plugins.length, pluginNames),
            width: 440
        });
    },

    displayUnknownLicensePluginsNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.no_license, plugins.length, pluginNames),
            width: 440,
            onCloseButton: function() {
                Ext.Msg.alert(
                    me.snippets.unknown_license,
                    me.snippets.confirm_open_pluginmanager,
                    function() {
                        me.openPluginManager(plugins);
                    }
                );
            }
        });

    },

    /**
     * Creates a string with all plugin names as a string separated by a defined separator, the default value for the
     * separator is a ','
     * @param plugins
     * @param separator
     * @returns [string]
     */
    getPluginNamesMessage: function (plugins, separator) {
        separator = (typeof separator == 'undefined' ? ',' : separator);

        var pluginNameList = plugins.map(function (plugin) {
            return plugin.label;
        });
        return pluginNameList.join(separator);
    },

    /**
     * Filters the plugin array for expired plugins or soon expired plugins
     * @param plugins
     * @param expired
     * @returns [object]
     */
    filterExpiredPlugins: function (plugins, expired) {
        if (expired) {
            return plugins.filter(function (plugin) {
                if (plugin.expired && plugin.daysLeft == 0) {
                    return plugin;
                }
            });
        } else {
            return plugins.filter(function (plugin) {
                if (!plugin.expired && plugin.daysLeft > 0) {
                    return plugin;
                }
            });
        }
    },

    /**
     * Returns an array with all plugins names including the days left until the subscription will expire
     * @param plugins
     * @param separator
     * @returns [string]
     */
    getSoonExpiredPluginNamesMessage: function (plugins, separator) {
        var me = this;
        separator = (typeof separator == 'undefined' ? ',' : separator);

        var pluginNameList = plugins.map(function (plugin) {
            return plugin.label + ' (' + plugin.daysLeft + me.snippets.expired_soon_subscription_days_warning + ')';
        });

        return pluginNameList.join(separator);
    },

    /**
    * Sorts the plugins by days left ascending
    * @param a
    * @param b
    * @returns [number]
    */
    sortPluginsByDaysLeftCallback: function(a, b) {
        if (a.daysLeft  b.daysLeft) {
            return 1;
        }
        return 0;
    },

    getExpiredMode: function(expiredPlugins) {
        var modes = {
            'stop': parseInt('{config name="missingLicenseStopThreshold"}'),
            'warning': parseInt('{config name="missingLicenseWarningThreshold"}'),
        }, currentMode = 'normal';

        Object.keys(modes).forEach(function (mode) {
            if (currentMode !== 'normal') {
                return;
            }
            expiredPlugins.forEach(function (plugin) {
                if (plugin.dayDiff >= modes[mode]) {
                    currentMode = mode;
                }
            })
        });

        return currentMode;
    },

    openPluginManager: function() {
        Shopware.app.Application.addSubApplication({
                name: 'Shopware.apps.PluginManager'
            },
            undefined,
            function() {
                Ext.Function.defer(function () {
                    Shopware.app.Application.fireEvent('display-installed-plugins');
                }, 2000);
            }
        );
    }
});<commit_msg>NTR - Fix notice width<commit_after>/**
 * Shopware 5
 * Copyright (c) shopware AG
 *
 * According to our dual licensing model, this program can be used either
 * under the terms of the GNU Affero General Public License, version 3,
 * or under a proprietary license.
 *
 * The texts of the GNU Affero General Public License with an additional
 * permission and of our proprietary license can be found at and
 * in the LICENSE file you have received along with this program.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * "Shopware" is a registered trademark of shopware AG.
 * The licensing of the program under the AGPLv3 does not imply a
 * trademark license. Therefore any rights, title and interest in
 * our trademarks remain entirely with us.
 */

//{namespace name=backend/index/view/menu}

Ext.define('Shopware.notification.SubscriptionWarning', {

    snippets: {
        licence_upgrade_warning: '[0]x plugin(s) require a licence upgrade.

Required upgrades:
[1]',
        subscription_warning: 'Subscription(s) for [0]x plugin(s) are expired.

Expired plugins:
[1]',
        expired_soon_subscription_warning: 'Subscription(s) for [0]x plugin(s) expire in a few days.

Soon expired plugins:
[1]',
        expired_soon_subscription_days_warning: ' days',
        invalid_licence: 'Licence(s) of [0]x plugin(s) are invalid.

Invalid licence(s):
[1]',
        shop_license_upgrade : 'The license upgrade for the shop hasn\'t been executed yet.',
        no_license: 'You may be a victim of counterfeiting.

No valid license found for plugins:
[1]',
        expiring_license: 'Expiring license(s)',
        expired_license: 'Expired license(s)',
        expiring_license_warning: 'License(s) of [0]x plugin(s) are soon expiring.

Soon expired license(s):
[1]',
        expired_license_warning: 'At least one license of your used plugins has expired.
Check this in your Shopware account under "Licenses > Licenses" and update your license immediately.',
        unknown_license: 'Unlicensed plugins',
        confirm_open_pluginmanager: 'You have installed unlicensed plugins. Do you want to open the Plugin Manager now to check your plugins?',
        subscription: 'Subscription',
        subscription_hide_message: 'Would you like to hide this message for a week?',
        openPluginOverview: 'Open plugin overview',
        importantInformation: 'Important Information',
        noShopSecretWarning: 'In order to receive information about updates and install plugins, you need to log in to your Shopware account. If you don\'t have a Shopware account yet, you can easily register.',
        login: 'Login now'
    },

    /**
     * Check if any plugins need to be upgraded
     */
    check: function () {
        var me = this;

        me.getPluginInformation(function (data) {
            var pluginData = me.preparePluginData(data);
            pluginData.expiredPluginSubscriptions.sort(me.sortPluginsByDaysLeftCallback);
            me.displayNotices(pluginData, data);
        });
    },

    checkSecret: function () {
        Ext.Ajax.request({
            url: '{url controller="PluginManager" action="checkSecret"}'
        });
    },

    preparePluginData: function(data) {
        var preparedData = {
                isShopUpgraded: data.isShopUpgraded,
                notUpgradedPlugins: [],
                wrongVersionPlugins: [],
                expiredPluginSubscriptions: [],
                unknownLicensePlugins: [],
                expiredLicensePlugins: []
            },
            plugins = data.plugins,
            today = Ext.Date.clearTime(new Date()),
            i = 0,
            count = plugins.length;

        for (i; i < count; i++) {
            var plugin = plugins[i];
            if (plugin.subscriptionUpgradeRequired) {
                preparedData.notUpgradedPlugins.push({
                    label: plugin.label
                });
            }
            if (plugin.wrongSubscription) {
                preparedData.wrongVersionPlugins.push({
                    label: plugin.label
                });
            }
            if (!Ext.isEmpty(plugin.subscriptionExpiration)) {
                var subscriptionExpirationDate = Ext.Date.parse(plugin.subscriptionExpiration, 'Y-m-d'),
                    isSubscriptionExpired, daysDiffSubscription;

                if (!Ext.isEmpty(subscriptionExpirationDate)) {
                    isSubscriptionExpired = subscriptionExpirationDate < today;
                    daysDiffSubscription = Math.round(Math.abs((subscriptionExpirationDate.getTime() - today.getTime())/(1000 * 60 * 60 * 24)));

                    if (isSubscriptionExpired) {
                        preparedData.expiredPluginSubscriptions.push({
                            label: plugin.label,
                            expired: isSubscriptionExpired,
                            daysLeft: isSubscriptionExpired ? 0 : daysDiffSubscription,
                            dayDiff: daysDiffSubscription
                        });
                    }
                }
            }
            if (plugin.unknownLicense) {
                preparedData.unknownLicensePlugins.push({
                    label: plugin.label
                });
            }
            if (!Ext.isEmpty(plugin.licenseExpiration)) {
                var licenseExpiration = Ext.Date.parse(plugin.licenseExpiration, 'Y-m-d'),
                    isLicenseExpired, daysDiffLicense;

                if (!Ext.isEmpty(licenseExpiration)) {
                    isLicenseExpired = licenseExpiration < today;
                    daysDiffLicense = Math.round(Math.abs((licenseExpiration.getTime() - today.getTime())/(1000 * 60 * 60 * 24)));

                    if (!isLicenseExpired && daysDiffLicense > 14) {
                        continue;
                    }

                    preparedData.expiredLicensePlugins.push({
                        label: plugin.label,
                        expired: isLicenseExpired,
                        daysLeft: isLicenseExpired ? 0 : daysDiffLicense,
                        dayDiff: daysDiffLicense
                    });
                }
            }
        }

        return preparedData;
    },

    getPluginInformation: function (callback) {
        var me = this;

        Ext.Ajax.request({
            url: '{url controller="PluginManager" action="getPluginInformation"}',
            success: function (response) {
                var responseData = Ext.decode(response.responseText);

                if (!responseData || Ext.isEmpty(responseData.data)) {
                    return;
                }

                if (responseData.data.shopSecretMissing) {
                    Shopware.Notification.createStickyGrowlMessage({
                        title: me.snippets.importantInformation,
                        text: me.snippets.noShopSecretWarning,
                        width: 460,
                        btnDetail: {
                            text: me.snippets.login,
                            callback: function () {
                                Shopware.app.Application.addSubApplication({
                                    name: 'Shopware.apps.PluginManager',
                                    params: {
                                        openLogin: true
                                    }
                                });
                            }
                        }
                    });
                }

                if (responseData.success === true && responseData.data) {
                    callback(responseData.data);
                }
            }
        });
    },

    displayNotices: function(data, rawData) {
        var me = this,
            expiredLicensePlugins = me.filterExpiredPlugins(data.expiredLicensePlugins, true),
            soonExpiredLicensePlugins = me.filterExpiredPlugins(data.expiredLicensePlugins, false),
            expiredPlugins = me.filterExpiredPlugins(data.expiredPluginSubscriptions, true),
            soonExpiredPlugins = me.filterExpiredPlugins(data.expiredPluginSubscriptions, false);

        if(data.isShopUpgraded == false) {
            me.displayShopNotUpgradedShopMessage();
        }

        if (data.notUpgradedPlugins && data.notUpgradedPlugins.length > 0) {
            me.displayNotUpgradedNotice(data.notUpgradedPlugins);
        }

        if (data.wrongVersionPlugins && data.wrongVersionPlugins.length > 0) {
            me.displayWrongVersionNotice(data.wrongVersionPlugins);
        }

        if (expiredPlugins && expiredPlugins.length > 0) {
            me.displaySubscriptionNotice(expiredPlugins);
        }

        if (soonExpiredPlugins && soonExpiredPlugins.length > 0) {
            me.displayExpiredSoonSubscriptionNotice(soonExpiredPlugins);
        }

        if (expiredLicensePlugins && expiredLicensePlugins.length > 0) {
            if (!rawData.live) {
                return;
            }

            switch (me.getExpiredMode(expiredLicensePlugins)) {
                case 'stop':
                    Ext.create('Shopware.window.ExpiredPluginStop');
                    break;
                case 'warning':
                    Ext.create('Shopware.window.ExpiredPluginWarning');
                    break;

                default:
                    me.displayExpiredLicensePluginsNotice(expiredLicensePlugins);
            }
        }

        if (soonExpiredLicensePlugins && soonExpiredLicensePlugins.length > 0) {
            me.displaySoonExpiredLicensePluginsNotice(soonExpiredLicensePlugins);
        }

        if (data.unknownLicensePlugins && data.unknownLicensePlugins.length > 0) {
            me.displayUnknownLicensePluginsNotice(data.unknownLicensePlugins);
        }
    },

    displayWrongVersionNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.invalid_licence, plugins.length, pluginNames),
            width: 440
        });
    },

    displayNotUpgradedNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.licence_upgrade_warning, plugins.length, pluginNames),
            width: 440
        });

    },

    displaySubscriptionNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        if (Ext.util.Cookies.get('hideSubscriptionNotice') !== null) {
            return;
        }

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.subscription_warning, plugins.length, pluginNames),
            width: 440,
            onCloseButton: function() {
                Ext.Msg.confirm(
                    me.snippets.subscription,
                    me.snippets.subscription_hide_message,
                    function(answer) {
                        if (answer === 'yes') {
                            var currentDate = new Date();
                            currentDate.setDate(currentDate.getDate() + 7);
                            Ext.util.Cookies.set('hideSubscriptionNotice', 1, currentDate);
                        }
                    }
                );
            }
        });
    },

    displayExpiredSoonSubscriptionNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getSoonExpiredPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.expired_soon_subscription_warning, plugins.length, pluginNames),
            width: 440
        });
    },

    displayShopNotUpgradedShopMessage: function(data) {
        var me = this;
        Shopware.Notification.createStickyGrowlMessage({
            text: '' + me.snippets.shop_license_upgrade + '',
            width: 440
        });
    },

    displayExpiredLicensePluginsNotice: function() {
        var me = this;

        Shopware.Notification.createStickyGrowlMessage({
            title: me.snippets.expired_license,
            text: me.snippets.expired_license_warning,
            width: 440,
            btnDetail: {
                text: me.snippets.openPluginOverview,
                callback: function () {
                    Shopware.app.Application.addSubApplication({
                        name: 'Shopware.apps.PluginManager',
                        action: 'ExpiredPlugins'
                    });
                }
            }
        });
    },

    displaySoonExpiredLicensePluginsNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getSoonExpiredPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            title: me.snippets.expiring_license,
            text: Ext.String.format(me.snippets.expiring_license_warning, plugins.length, pluginNames),
            width: 440
        });
    },

    displayUnknownLicensePluginsNotice: function(plugins) {
        var me          = this,
            pluginNames = me.getPluginNamesMessage(plugins, '
');

        Shopware.Notification.createStickyGrowlMessage({
            text: Ext.String.format(me.snippets.no_license, plugins.length, pluginNames),
            width: 440,
            onCloseButton: function() {
                Ext.Msg.alert(
                    me.snippets.unknown_license,
                    me.snippets.confirm_open_pluginmanager,
                    function() {
                        me.openPluginManager(plugins);
                    }
                );
            }
        });

    },

    /**
     * Creates a string with all plugin names as a string separated by a defined separator, the default value for the
     * separator is a ','
     * @param plugins
     * @param separator
     * @returns [string]
     */
    getPluginNamesMessage: function (plugins, separator) {
        separator = (typeof separator == 'undefined' ? ',' : separator);

        var pluginNameList = plugins.map(function (plugin) {
            return plugin.label;
        });
        return pluginNameList.join(separator);
    },

    /**
     * Filters the plugin array for expired plugins or soon expired plugins
     * @param plugins
     * @param expired
     * @returns [object]
     */
    filterExpiredPlugins: function (plugins, expired) {
        if (expired) {
            return plugins.filter(function (plugin) {
                if (plugin.expired && plugin.daysLeft == 0) {
                    return plugin;
                }
            });
        } else {
            return plugins.filter(function (plugin) {
                if (!plugin.expired && plugin.daysLeft > 0) {
                    return plugin;
                }
            });
        }
    },

    /**
     * Returns an array with all plugins names including the days left until the subscription will expire
     * @param plugins
     * @param separator
     * @returns [string]
     */
    getSoonExpiredPluginNamesMessage: function (plugins, separator) {
        var me = this;
        separator = (typeof separator == 'undefined' ? ',' : separator);

        var pluginNameList = plugins.map(function (plugin) {
            return plugin.label + ' (' + plugin.daysLeft + me.snippets.expired_soon_subscription_days_warning + ')';
        });

        return pluginNameList.join(separator);
    },

    /**
    * Sorts the plugins by days left ascending
    * @param a
    * @param b
    * @returns [number]
    */
    sortPluginsByDaysLeftCallback: function(a, b) {
        if (a.daysLeft  b.daysLeft) {
            return 1;
        }
        return 0;
    },

    getExpiredMode: function(expiredPlugins) {
        var modes = {
            'stop': parseInt('{config name="missingLicenseStopThreshold"}'),
            'warning': parseInt('{config name="missingLicenseWarningThreshold"}'),
        }, currentMode = 'normal';

        Object.keys(modes).forEach(function (mode) {
            if (currentMode !== 'normal') {
                return;
            }
            expiredPlugins.forEach(function (plugin) {
                if (plugin.dayDiff >= modes[mode]) {
                    currentMode = mode;
                }
            })
        });

        return currentMode;
    },

    openPluginManager: function() {
        Shopware.app.Application.addSubApplication({
                name: 'Shopware.apps.PluginManager'
            },
            undefined,
            function() {
                Ext.Function.defer(function () {
                    Shopware.app.Application.fireEvent('display-installed-plugins');
                }, 2000);
            }
        );
    }
});<|endoftext|>


<commit_before>/*jslint browser: true, for: true, node: true */
/*global window */

/*!

   hlsjs engine plugin for Flowplayer HTML5

   Copyright (c) 2015, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   Includes hls.min.js
   Copyright (c) 2015 Dailymotion (http://www.dailymotion.com)
   https://github.com/dailymotion/hls.js/blob/master/LICENSE

   requires:
   - Flowplayer HTML5 version 6.x or greater
   - hls.js https://github.com/dailymotion/hls.js
   revision: $GIT_ID$

*/

(function (flowplayer, Hls) {
    "use strict";
    var engineName = "hlsjs",
        hlsconf,
        common = flowplayer.common,
        extend = flowplayer.extend,
        version = flowplayer.version,

        isHlsType = function (typ) {
            return typ.toLowerCase().indexOf("mpegurl") > -1;
        },

        engineImpl = function hlsjsEngine(player, root) {
            var bean = flowplayer.bean,
                videoTag,
                hls,

                pre604 = /^6\.0\.[0-3]$/.test(version),
                bc = common.css(root, 'backgroundColor'),
                has_bg = common.css(root, 'backgroundImage') !== "none" ||
                        (bc && bc !== "rgba(0,0,0,0)" && bc !== "transparent"),
                posterCondition = has_bg && !player.conf.splash
                        && (!pre604 || (pre604 && !player.conf.autoplay)),

                posterHack = function (e) {
                    // assert that poster is set regardless of client of
                    // video loading delay
                    setTimeout(function () {
                        var posterClass = "is-poster";

                        if (!pre604) {
                            // must set this ourselves
                            player.poster = true;
                        }
                        if (!player.conf.autoplay || e.type === "stop") {
                            common.addClass(root, posterClass);
                            player.one("resume." + engineName, function () {
                                player.off("ready." + engineName);
                                common.removeClass(root, posterClass);
                            });
                        }
                    }, 0);
                },

                engine = {
                    engineName: engineName,

                    pick: function (sources) {
                        var i,
                            source;

                        for (i = 0; i < sources.length; i += 1) {
                            source = sources[i];
                            if (isHlsType(source.type)
                                    && (!source.engine || source.engine === engineName)) {
                                if (typeof source.src === 'string') {
                                    source.src = common.createAbsoluteUrl(source.src);
                                }
                                return source;
                            }
                        }
                    },

                    load: function (video) {
                        var init = !hls,
                            conf = player.conf,
                            hlsClientConf = extend({}, hlsconf),
                            hlsParams = ["autoLevelCapping", "startLevel", "strict"];

                        if (init) {
                            common.removeNode(common.findDirect("video", root)[0]
                                    || common.find(".fp-player > video", root)[0]);
                            videoTag = common.createElement("video", {
                                className: "fp-engine " + engineName + "-engine",
                                autoplay: conf.autoplay
                                    ? "autoplay"
                                    : false
                            });
                            videoTag.setAttribute("x-webkit-airplay", "allow");

                        } else {
                            hls.destroy();
                        }

                        bean.on(videoTag, "play", function () {
                            player.trigger('resume', [player]);
                        });
                        bean.on(videoTag, "pause", function () {
                            player.trigger('pause', [player]);
                        });
                        bean.on(videoTag, "timeupdate", function () {
                            player.trigger('progress', [player, videoTag.currentTime]);
                        });
                        bean.on(videoTag, "loadeddata", function () {
                            video = extend(video, {
                                duration: videoTag.duration,
                                seekable: videoTag.seekable.end(null),
                                width: videoTag.videoWidth,
                                height: videoTag.videoHeight,
                                url: videoTag.currentSrc
                            });
                            player.trigger('ready', [player, video]);
                        });
                        bean.on(videoTag, "seeked", function () {
                            player.trigger('seek', [player, videoTag.currentTime]);
                        });
                        bean.on(videoTag, "progress", function (e) {
                            var ct = videoTag.currentTime,
                                buffer = 0,
                                buffend,
                                buffered,
                                last,
                                i;

                            try {
                                buffered = videoTag.buffered;
                                last = buffered.length - 1;
                                buffend = 0;
                                // cycle through time ranges to obtain buffer
                                // nearest current time
                                if (ct) {
                                    for (i = last; i > -1; i -= 1) {
                                        buffend = buffered.end(i);

                                        if (buffend >= ct) {
                                            buffer = buffend;
                                        }
                                    }
                                }
                            } catch (ignored) {}

                            video.buffer = buffer;
                            player.trigger('buffer', [player, e]);
                        });
                        bean.on(videoTag, "ended", function () {
                            player.trigger('finish', [player]);
                        });
                        bean.on(videoTag, "volumechange", function () {
                            player.trigger('volume', [player, videoTag.volume]);
                        });

                        hlsParams.forEach(function (key) {
                            delete hlsClientConf[key];
                        });
                        hls = new Hls(hlsClientConf);

                        hlsParams.forEach(function (key) {
                            var value = hlsconf[key];

                            switch (key) {
                            case "autoLevelCapping":
                                if (value === false) {
                                    value = -1;
                                }
                                break;
                            case "startLevel":
                                switch (value) {
                                case "auto":
                                    value = -1;
                                    break;
                                case "firstLevel":
                                    value = undefined;
                                    break;
                                }
                                break;
                            }

                            if (value !== undefined) {
                                hls[key] = value;
                            }
                        });

                        hls.on(Hls.Events.ERROR, function (e, data) {
                            var fperr,
                                errobj = {};

                            if (data.fatal || hlsconf.strict) {
                                switch (data.type) {
                                case Hls.ErrorTypes.NETWORK_ERROR:
                                    if (data.frag && data.frag.url) {
                                        errobj.url = data.frag.url;
                                        fperr = 2;
                                    } else {
                                        fperr = 4;
                                    }
                                    break;
                                case Hls.ErrorTypes.MEDIA_ERROR:
                                    fperr = 3;
                                    break;
                                default:
                                    fperr = 5;
                                    break;
                                }
                                errobj.code = fperr;
                                if (fperr > 2) {
                                    errobj.video = extend(video, {
                                        src: video.src,
                                        url: data.url || video.src
                                    });
                                }
                                player.trigger('error', [player, errobj]);
                            }
                            /* TODO: */
                            // log non fatals
                        });

                        if (init) {
                            common.prepend(common.find(".fp-player", root)[0], videoTag);
                        }

                        hls.loadSource(video.src);
                        hls.attachVideo(videoTag);

                        if (videoTag.paused && (video.autoplay || conf.autoplay)) {
                            videoTag.play();
                        }
                    },

                    resume: function () {
                        videoTag.play();
                    },

                    pause: function () {
                        videoTag.pause();
                    },

                    seek: function (time) {
                        videoTag.currentTime = time;
                    },

                    volume: function (level) {
                        if (videoTag) {
                            videoTag.volume = level;
                        }
                    },

                    speed: function (val) {
                        videoTag.playbackRate = val;
                        player.trigger('speed', [player, val]);
                    },

                    unload: function () {
                        // hls conditional probably not needed once
                        // https://github.com/flowplayer/flowplayer/commit/871ff783a8f23aa603e1120f4319d4a892125b0a
                        // is released
                        if (hls) {
                            hls.destroy();
                            hls = 0;
                            bean.off(videoTag);
                            common.removeNode(videoTag);
                            videoTag = 0;
                        }
                    }
                };

            if (posterCondition) {
                player.on("ready." + engineName + " stop." + engineName, posterHack)
                    .one("seek." + engineName, posterHack);
            }

            return engine;
        };

    if (Hls.isSupported() && version.indexOf("5.") !== 0) {
        // only load engine if it can be used
        engineImpl.engineName = engineName; // must be exposed
        engineImpl.canPlay = function (type, conf) {
            var IE11 = navigator.userAgent.indexOf("Trident/7") > -1;

            if (conf[engineName] === false || conf.clip[engineName] === false) {
                // engine disabled for player or clip
                return false;
            }

            // merge hlsjs clip config at earliest opportunity
            hlsconf = extend({}, flowplayer.conf[engineName], conf[engineName], conf.clip[engineName]);

            // support Safari only when hlsjs debugging
            // https://github.com/dailymotion/hls.js/issues/9
            return isHlsType(type) &&
                    (IE11 || !flowplayer.support.browser.safari || hlsconf.debug);
        };

        // put on top of engine stack
        // so hlsjs is tested before html5 video hls and flash hls
        flowplayer.engines.unshift(engineImpl);

    }

}.apply(null, (typeof module === 'object' && module.exports)
    ? [require('flowplayer'), require('hls.js')]
    : [window.flowplayer, window.Hls]));
<commit_msg>Spaces in rgba argument mandatory for read check (#16)<commit_after>/*jslint browser: true, for: true, node: true */
/*global window */

/*!

   hlsjs engine plugin for Flowplayer HTML5

   Copyright (c) 2015, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   Includes hls.min.js
   Copyright (c) 2015 Dailymotion (http://www.dailymotion.com)
   https://github.com/dailymotion/hls.js/blob/master/LICENSE

   requires:
   - Flowplayer HTML5 version 6.x or greater
   - hls.js https://github.com/dailymotion/hls.js
   revision: $GIT_ID$

*/

(function (flowplayer, Hls) {
    "use strict";
    var engineName = "hlsjs",
        hlsconf,
        common = flowplayer.common,
        extend = flowplayer.extend,
        version = flowplayer.version,

        isHlsType = function (typ) {
            return typ.toLowerCase().indexOf("mpegurl") > -1;
        },

        engineImpl = function hlsjsEngine(player, root) {
            var bean = flowplayer.bean,
                videoTag,
                hls,

                pre604 = /^6\.0\.[0-3]$/.test(version),
                bc = common.css(root, 'backgroundColor'),
                // spaces in rgba arg mandatory for recognition
                has_bg = common.css(root, 'backgroundImage') !== "none" ||
                        (bc && bc !== "rgba(0, 0, 0, 0)" && bc !== "transparent"),
                posterCondition = has_bg && !player.conf.splash
                        && (!pre604 || (pre604 && !player.conf.autoplay)),

                posterHack = function (e) {
                    // assert that poster is set regardless of client of
                    // video loading delay
                    setTimeout(function () {
                        var posterClass = "is-poster";

                        if (!pre604) {
                            // must set this ourselves
                            player.poster = true;
                        }
                        if (!player.conf.autoplay || e.type === "stop") {
                            common.addClass(root, posterClass);
                            player.one("resume." + engineName, function () {
                                player.off("ready." + engineName);
                                common.removeClass(root, posterClass);
                            });
                        }
                    }, 0);
                },

                engine = {
                    engineName: engineName,

                    pick: function (sources) {
                        var i,
                            source;

                        for (i = 0; i < sources.length; i += 1) {
                            source = sources[i];
                            if (isHlsType(source.type)
                                    && (!source.engine || source.engine === engineName)) {
                                if (typeof source.src === 'string') {
                                    source.src = common.createAbsoluteUrl(source.src);
                                }
                                return source;
                            }
                        }
                    },

                    load: function (video) {
                        var init = !hls,
                            conf = player.conf,
                            hlsClientConf = extend({}, hlsconf),
                            hlsParams = ["autoLevelCapping", "startLevel", "strict"];

                        if (init) {
                            common.removeNode(common.findDirect("video", root)[0]
                                    || common.find(".fp-player > video", root)[0]);
                            videoTag = common.createElement("video", {
                                className: "fp-engine " + engineName + "-engine",
                                autoplay: conf.autoplay
                                    ? "autoplay"
                                    : false
                            });
                            videoTag.setAttribute("x-webkit-airplay", "allow");

                        } else {
                            hls.destroy();
                        }

                        bean.on(videoTag, "play", function () {
                            player.trigger('resume', [player]);
                        });
                        bean.on(videoTag, "pause", function () {
                            player.trigger('pause', [player]);
                        });
                        bean.on(videoTag, "timeupdate", function () {
                            player.trigger('progress', [player, videoTag.currentTime]);
                        });
                        bean.on(videoTag, "loadeddata", function () {
                            video = extend(video, {
                                duration: videoTag.duration,
                                seekable: videoTag.seekable.end(null),
                                width: videoTag.videoWidth,
                                height: videoTag.videoHeight,
                                url: videoTag.currentSrc
                            });
                            player.trigger('ready', [player, video]);
                        });
                        bean.on(videoTag, "seeked", function () {
                            player.trigger('seek', [player, videoTag.currentTime]);
                        });
                        bean.on(videoTag, "progress", function (e) {
                            var ct = videoTag.currentTime,
                                buffer = 0,
                                buffend,
                                buffered,
                                last,
                                i;

                            try {
                                buffered = videoTag.buffered;
                                last = buffered.length - 1;
                                buffend = 0;
                                // cycle through time ranges to obtain buffer
                                // nearest current time
                                if (ct) {
                                    for (i = last; i > -1; i -= 1) {
                                        buffend = buffered.end(i);

                                        if (buffend >= ct) {
                                            buffer = buffend;
                                        }
                                    }
                                }
                            } catch (ignored) {}

                            video.buffer = buffer;
                            player.trigger('buffer', [player, e]);
                        });
                        bean.on(videoTag, "ended", function () {
                            player.trigger('finish', [player]);
                        });
                        bean.on(videoTag, "volumechange", function () {
                            player.trigger('volume', [player, videoTag.volume]);
                        });

                        hlsParams.forEach(function (key) {
                            delete hlsClientConf[key];
                        });
                        hls = new Hls(hlsClientConf);

                        hlsParams.forEach(function (key) {
                            var value = hlsconf[key];

                            switch (key) {
                            case "autoLevelCapping":
                                if (value === false) {
                                    value = -1;
                                }
                                break;
                            case "startLevel":
                                switch (value) {
                                case "auto":
                                    value = -1;
                                    break;
                                case "firstLevel":
                                    value = undefined;
                                    break;
                                }
                                break;
                            }

                            if (value !== undefined) {
                                hls[key] = value;
                            }
                        });

                        hls.on(Hls.Events.ERROR, function (e, data) {
                            var fperr,
                                errobj = {};

                            if (data.fatal || hlsconf.strict) {
                                switch (data.type) {
                                case Hls.ErrorTypes.NETWORK_ERROR:
                                    if (data.frag && data.frag.url) {
                                        errobj.url = data.frag.url;
                                        fperr = 2;
                                    } else {
                                        fperr = 4;
                                    }
                                    break;
                                case Hls.ErrorTypes.MEDIA_ERROR:
                                    fperr = 3;
                                    break;
                                default:
                                    fperr = 5;
                                    break;
                                }
                                errobj.code = fperr;
                                if (fperr > 2) {
                                    errobj.video = extend(video, {
                                        src: video.src,
                                        url: data.url || video.src
                                    });
                                }
                                player.trigger('error', [player, errobj]);
                            }
                            /* TODO: */
                            // log non fatals
                        });

                        if (init) {
                            common.prepend(common.find(".fp-player", root)[0], videoTag);
                        }

                        hls.loadSource(video.src);
                        hls.attachVideo(videoTag);

                        if (videoTag.paused && (video.autoplay || conf.autoplay)) {
                            videoTag.play();
                        }
                    },

                    resume: function () {
                        videoTag.play();
                    },

                    pause: function () {
                        videoTag.pause();
                    },

                    seek: function (time) {
                        videoTag.currentTime = time;
                    },

                    volume: function (level) {
                        if (videoTag) {
                            videoTag.volume = level;
                        }
                    },

                    speed: function (val) {
                        videoTag.playbackRate = val;
                        player.trigger('speed', [player, val]);
                    },

                    unload: function () {
                        // hls conditional probably not needed once
                        // https://github.com/flowplayer/flowplayer/commit/871ff783a8f23aa603e1120f4319d4a892125b0a
                        // is released
                        if (hls) {
                            hls.destroy();
                            hls = 0;
                            bean.off(videoTag);
                            common.removeNode(videoTag);
                            videoTag = 0;
                        }
                    }
                };

            if (posterCondition) {
                player.on("ready." + engineName + " stop." + engineName, posterHack)
                    .one("seek." + engineName, posterHack);
            }

            return engine;
        };

    if (Hls.isSupported() && version.indexOf("5.") !== 0) {
        // only load engine if it can be used
        engineImpl.engineName = engineName; // must be exposed
        engineImpl.canPlay = function (type, conf) {
            var IE11 = navigator.userAgent.indexOf("Trident/7") > -1;

            if (conf[engineName] === false || conf.clip[engineName] === false) {
                // engine disabled for player or clip
                return false;
            }

            // merge hlsjs clip config at earliest opportunity
            hlsconf = extend({}, flowplayer.conf[engineName], conf[engineName], conf.clip[engineName]);

            // support Safari only when hlsjs debugging
            // https://github.com/dailymotion/hls.js/issues/9
            return isHlsType(type) &&
                    (IE11 || !flowplayer.support.browser.safari || hlsconf.debug);
        };

        // put on top of engine stack
        // so hlsjs is tested before html5 video hls and flash hls
        flowplayer.engines.unshift(engineImpl);

    }

}.apply(null, (typeof module === 'object' && module.exports)
    ? [require('flowplayer'), require('hls.js')]
    : [window.flowplayer, window.Hls]));
<|endoftext|>