<commit_before>// Laconic simplifies the generation of DOM content.
(function(context) {

  // properly-cased attribute names for IE setAttribute support
  var attributeMap = {
    'acceptcharset'     : 'acceptCharset',
    'accesskey'         : 'accessKey',
    'allowtransparency' : 'allowTransparency',
    'bgcolor'           : 'bgColor',
    'cellpadding'       : 'cellPadding',
    'cellspacing'       : 'cellSpacing',
    'class'             : 'className',
    'classname'         : 'className',
    'colspan'           : 'colSpan',
    'csstext'           : 'style',
    'defaultchecked'    : 'defaultChecked',
    'defaultselected'   : 'defaultSelected',
    'defaultvalue'      : 'defaultValue',
    'for'               : 'htmlFor',
    'frameborder'       : 'frameBorder',
    'hspace'            : 'hSpace',
    'htmlfor'           : 'htmlFor',
    'longdesc'          : 'longDesc',
    'maxlength'         : 'maxLength',
    'marginwidth'       : 'marginWidth',
    'marginheight'      : 'marginHeight',
    'noresize'          : 'noResize',
    'noshade'           : 'noShade',
    'readonly'          : 'readOnly',
    'rowspan'           : 'rowSpan',
    'tabindex'          : 'tabIndex',
    'valign'            : 'vAlign',
    'vspace'            : 'vSpace'
  };

  // The laconic function serves as a generic method for generating
  // DOM content, and also as a placeholder for helper functions.
  //
  // The first parameter MUST be a string specifying the element's
  // tag name.
  //
  // An optional object of element attributs may follow directly
  // after the tag name.
  //
  // Additional arguments will be considered children of the new
  // element and may consist of elements, strings, or numbers.
  //
  // for example:
  // laconic('div', {'class' : 'foo'}, 'bar');
  function laconic() {

    // create a new element of the requested type
    var el = document.createElement(arguments[0]);

    // walk through the rest of the arguments
    for(var i=1; i<arguments.length; i++) {
      var arg = arguments[i];
      if(arg === null || arg === undefined) continue;

      // if the argument is a dom node, we simply append it
      if(arg.nodeType === 1) {
        el.appendChild(arg);
      }

      // if the argument is a string or a number, we append it as
      // a new text node
      else if(
          (!!(arg === '' || (arg && arg.charCodeAt && arg.substr))) ||
          (!!(arg === 0  || (arg && arg.toExponential && arg.toFixed)))) {

        el.appendChild(document.createTextNode(arg));
      }

      // if the argument is a plain-old object, and we're processing the first
      // argument, then we apply the object's values as element attributes
      else if(i === 1 && typeof(arg) === 'object') {
        for(var key in arg) {
          if(arg.hasOwnProperty(key)) {
            var value = arg[key];
            if(value !== null && value !== undefined) {
              key = key.toLowerCase();
              key = attributeMap[key] || key;

              // if the key represents an event (onclick, onchange, etc)
              // we'll set the href to '#' if none is given, and we'll apply
              // the attribute directly to the element for IE7 support.
              var isEvent = key.charAt(0) === 'o' && key.charAt(1) === 'n';
              if(isEvent) {
                if(arg.href === undefined && key === 'onclick') {
                  el.setAttribute('href', '#');
                }
                el[key] = value;
              }

              // if we're setting the style attribute, we may need to
              // use the cssText property
              else if(key === 'style' && el.style.setAttribute) {
                el.style.setAttribute('cssText', value);
              }

              // if we're setting an attribute that's not properly supported
              // by IE7's setAttribute implementation, then we apply the
              // attribute directly to the element
              else if(key === 'className' || key === 'htmlFor') {
                el[key] = value;
              }

              // otherwise, we use the standard setAttribute
              else {
                el.setAttribute(key, value);
              }
            }
          }
        }
      }

      // if the argument is an array, we append each element
      else if(Object.prototype.toString.call(arg) === '[object Array]') {
        for(var j=0; j<arg.length; j++) {
          var child = arg[j];
          if(child.nodeType === 1) {
            el.appendChild(child);
          }
        }
      }
    }

    // Add an appendTo method to the newly created element, which will allow
    // the DOM insertion to be method chained to the creation.  For example:
    // $el.div('foo').appendTo(document.body);
    el.appendTo = function(parentNode) {
      if(parentNode.nodeType === 1 && this.nodeType === 1) {
        parentNode.appendChild(this);
      }
      return el;
    };

    return el;
  }

  // registers a new 'tag' that can be used to automate
  // the creation of a known element hierarchy
  laconic.registerTag = function(name, renderer) {
    if(!laconic[name]) {
      laconic[name] = function() {
        var el = laconic('div', {'class' : name});
        renderer.apply(el, Array.prototype.slice.call(arguments));
        return el;
      };
    }
  };

  // html 4 tags
  var deprecatedTags = ['acronym', 'applet', 'basefont', 'big', 'center', 'dir',
    'font', 'frame', 'frameset', 'noframes', 'strike', 'tt', 'u', 'xmp'];

  // html 5 tags
  var tags = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b',
    'base', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption',
    'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del',
    'details', 'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset',
    'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
    'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img',
    'input', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li', 'link', 'map',
    'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol',
    'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp',
    'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small',
    'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table',
    'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr',
    'ul', 'var', 'video', 'wbr'].concat(deprecatedTags);

  // add our tag methods to the laconic object
  var makeApply = function(tagName) {
    return function() {
      return laconic.apply(this,
        [tagName].concat(Array.prototype.slice.call(arguments)));
    };
  };

  for(var i=0; i<tags.length; i++) {
    laconic[tags[i]] = makeApply(tags[i]);
  }

  // If we're in a CommonJS environment, we export our laconic methods
  if(typeof module !== 'undefined' && module.exports) {
    module.exports = laconic;
  }

  // otherwise, we attach them to the top level $.el namespace
  else {
    var dollar = context.$ || {};
    dollar.el = laconic;
    context.$ = dollar;
  }
}(this));
<commit_msg>renaming createTag to createElement<commit_after>// Laconic simplifies the generation of DOM content.
(function(context) {

  // properly-cased attribute names for IE setAttribute support
  var attributeMap = {
    'acceptcharset'     : 'acceptCharset',
    'accesskey'         : 'accessKey',
    'allowtransparency' : 'allowTransparency',
    'bgcolor'           : 'bgColor',
    'cellpadding'       : 'cellPadding',
    'cellspacing'       : 'cellSpacing',
    'class'             : 'className',
    'classname'         : 'className',
    'colspan'           : 'colSpan',
    'csstext'           : 'style',
    'defaultchecked'    : 'defaultChecked',
    'defaultselected'   : 'defaultSelected',
    'defaultvalue'      : 'defaultValue',
    'for'               : 'htmlFor',
    'frameborder'       : 'frameBorder',
    'hspace'            : 'hSpace',
    'htmlfor'           : 'htmlFor',
    'longdesc'          : 'longDesc',
    'maxlength'         : 'maxLength',
    'marginwidth'       : 'marginWidth',
    'marginheight'      : 'marginHeight',
    'noresize'          : 'noResize',
    'noshade'           : 'noShade',
    'readonly'          : 'readOnly',
    'rowspan'           : 'rowSpan',
    'tabindex'          : 'tabIndex',
    'valign'            : 'vAlign',
    'vspace'            : 'vSpace'
  };

  // The laconic function serves as a generic method for generating
  // DOM content, and also as a placeholder for helper functions.
  //
  // The first parameter MUST be a string specifying the element's
  // tag name.
  //
  // An optional object of element attributs may follow directly
  // after the tag name.
  //
  // Additional arguments will be considered children of the new
  // element and may consist of elements, strings, or numbers.
  //
  // for example:
  // laconic('div', {'class' : 'foo'}, 'bar');
  function laconic() {

    // create a new element of the requested type
    var el = document.createElement(arguments[0]);

    // walk through the rest of the arguments
    for(var i=1; i<arguments.length; i++) {
      var arg = arguments[i];
      if(arg === null || arg === undefined) continue;

      // if the argument is a dom node, we simply append it
      if(arg.nodeType === 1) {
        el.appendChild(arg);
      }

      // if the argument is a string or a number, we append it as
      // a new text node
      else if(
          (!!(arg === '' || (arg && arg.charCodeAt && arg.substr))) ||
          (!!(arg === 0  || (arg && arg.toExponential && arg.toFixed)))) {

        el.appendChild(document.createTextNode(arg));
      }

      // if the argument is a plain-old object, and we're processing the first
      // argument, then we apply the object's values as element attributes
      else if(i === 1 && typeof(arg) === 'object') {
        for(var key in arg) {
          if(arg.hasOwnProperty(key)) {
            var value = arg[key];
            if(value !== null && value !== undefined) {
              key = key.toLowerCase();
              key = attributeMap[key] || key;

              // if the key represents an event (onclick, onchange, etc)
              // we'll set the href to '#' if none is given, and we'll apply
              // the attribute directly to the element for IE7 support.
              var isEvent = key.charAt(0) === 'o' && key.charAt(1) === 'n';
              if(isEvent) {
                if(arg.href === undefined && key === 'onclick') {
                  el.setAttribute('href', '#');
                }
                el[key] = value;
              }

              // if we're setting the style attribute, we may need to
              // use the cssText property
              else if(key === 'style' && el.style.setAttribute) {
                el.style.setAttribute('cssText', value);
              }

              // if we're setting an attribute that's not properly supported
              // by IE7's setAttribute implementation, then we apply the
              // attribute directly to the element
              else if(key === 'className' || key === 'htmlFor') {
                el[key] = value;
              }

              // otherwise, we use the standard setAttribute
              else {
                el.setAttribute(key, value);
              }
            }
          }
        }
      }

      // if the argument is an array, we append each element
      else if(Object.prototype.toString.call(arg) === '[object Array]') {
        for(var j=0; j<arg.length; j++) {
          var child = arg[j];
          if(child.nodeType === 1) {
            el.appendChild(child);
          }
        }
      }
    }

    // Add an appendTo method to the newly created element, which will allow
    // the DOM insertion to be method chained to the creation.  For example:
    // $el.div('foo').appendTo(document.body);
    el.appendTo = function(parentNode) {
      if(parentNode.nodeType === 1 && this.nodeType === 1) {
        parentNode.appendChild(this);
      }
      return el;
    };

    return el;
  }

  // registers a new 'tag' that can be used to automate
  // the creation of a known element hierarchy
  laconic.registerElement= function(name, renderer) {
    if(!laconic[name]) {
      laconic[name] = function() {
        var el = laconic('div', {'class' : name});
        renderer.apply(el, Array.prototype.slice.call(arguments));
        return el;
      };
    }
  };

  // html 4 tags
  var deprecatedTags = ['acronym', 'applet', 'basefont', 'big', 'center', 'dir',
    'font', 'frame', 'frameset', 'noframes', 'strike', 'tt', 'u', 'xmp'];

  // html 5 tags
  var tags = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b',
    'base', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption',
    'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del',
    'details', 'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset',
    'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
    'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img',
    'input', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li', 'link', 'map',
    'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol',
    'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp',
    'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small',
    'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table',
    'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr',
    'ul', 'var', 'video', 'wbr'].concat(deprecatedTags);

  // add our tag methods to the laconic object
  var makeApply = function(tagName) {
    return function() {
      return laconic.apply(this,
        [tagName].concat(Array.prototype.slice.call(arguments)));
    };
  };

  for(var i=0; i<tags.length; i++) {
    laconic[tags[i]] = makeApply(tags[i]);
  }

  // If we're in a CommonJS environment, we export our laconic methods
  if(typeof module !== 'undefined' && module.exports) {
    module.exports = laconic;
  }

  // otherwise, we attach them to the top level $.el namespace
  else {
    var dollar = context.$ || {};
    dollar.el = laconic;
    context.$ = dollar;
  }
}(this));
<|endoftext|>


<commit_before>// I18N constants
// LANG: "fr", ENCODING: UTF-8
{
  "Bold": "Gras",
  "Italic": "Italique",
  "Underline": "Souligné",
  "Strikethrough": "Barré",
  "Subscript": "Indice",
  "Superscript": "Exposant",
  "Justify Left": "Aligner à gauche",
  "Justify Center": "Centrer",
  "Justify Right": "Aligner à droite",
  "Justify Full": "Justifier",
  "Ordered List": "Numérotation",
  "Bulleted List": "Puces",
  "Decrease Indent": "Diminuer le retrait",
  "Increase Indent": "Augmenter le retrait",
  "Font Color": "Couleur de police",
  "Background Color": "Surlignage",
  "Horizontal Rule": "Ligne horizontale",
  "Insert Web Link": "Insérer un lien",
  "Insert/Modify Image": "Insérer / Modifier une image",
  "Insert Table": "Insérer un tableau",
  "Toggle HTML Source": "Afficher / Masquer code source",
  "Enlarge Editor": "Agrandir l'éditeur",
  "About this editor": "A propos",
  "Help using editor": "Aide",
  "Current style": "Style courant",
  "Undoes your last action": "Annuler la dernière action",
  "Redoes your last action": "Répéter la dernière action",
  "Cut selection": "Couper la sélection",
  "Copy selection": "Copier la sélection",
  "Paste from clipboard": "Coller depuis le presse-papier",
  "Direction left to right": "Direction de gauche à droite",
  "Direction right to left": "Direction de droite à gauche",
  "Remove formatting": "Supprimer mise en forme",
  "Select all": "Tout sélectionner",
  "Print document": "Imprimer document",
  "Clear MSOffice tags": "Effacer tags MSOffice",
  "Clear Inline Font Specifications": "Supprimer paramètres inline de la fonte",
  "Split Block": "Séparer les blocks",
  "Toggle Borders": "Afficher / Masquer les bordures",
  "Save as": "Enregistrer sous",
  "Insert/Overwrite": "Insertion / Remplacement",
  "— format —": "— Format —",
  "Heading 1": "Titre 1",
  "Heading 2": "Titre 2",
  "Heading 3": "Titre 3",
  "Heading 4": "Titre 4",
  "Heading 5": "Titre 5",
  "Heading 6": "Titre 6",
  "Normal": "Normal",
  "Address": "Adresse",
  "Formatted": "Formaté",

  //dialogs
  "OK": "OK",
  "Cancel": "Annuler",
  "Path": "Chemin",
  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Vous êtes en MODE TEXTE.  Appuyez sur le bouton [<>] pour retourner au mode WYSIWYG.",
  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Le bouton Coller ne fonctionne pas sur les navigateurs basés sur Mozilla (pour des raisons de sécurité). Pressez simplement CTRL-V au clavier pour coller directement.",
  "Your Document is not well formed. Check JavaScript console for details.": "Le document est mal formé. Vérifiez la console JavaScript pour plus de détail.",

  "Alignment:": "Alignement",
  "Not set": "Indéfini",
  "Left": "Gauche",
  "Right": "Droite",
  "Texttop": "Texttop",
  "Absmiddle": "Absmiddle",
  "Baseline": "Baseline",
  "Absbottom": "Absbottom",
  "Bottom": "Bas",
  "Middle": "Milieu",
  "Top": "Haut",

  "Layout": "Mise en page",
  "Spacing": "Espacement",
  "Horizontal:": "Horizontal",
  "Horizontal padding": "Marge horizontale interne",
  "Vertical:": "Vertical",
  "Vertical padding": "Marge verticale interne",
  "Border thickness:": "Epaisseur bordure",
  "Leave empty for no border": "Laisser vide pour pas de bordure",

  //Insert Link
  "Insert/Modify Link": "Insérer / Modifier un lien",
  "None (use implicit)": "Aucune (implicite)",
  "New window (_blank)": "Nouvelle fenêtre (_blank)",
  "Same frame (_self)": "Même frame (_self)",
  "Top frame (_top)": "Frame principale (_top)",
  "Other": "Autre",
  "Target:": "Cible",
  "Title (tooltip):": "Texte alternatif",
  "URL:": "URL:",
  "You must enter the URL where this link points to": "Vous devez entrer l'URL de ce lien",
  "You need to select some text before creating a link": "Vous devez sélectionner du texte avant de créer un lien",

  // Insert Table
  "Insert Table": "Insérer un tableau",
  "Rows:": "Lignes",
  "Number of rows": "Nombre de lignes",
  "Cols:": "Colonnes",
  "Number of columns": "Nombre de colonnes",
  "Width:": "Largeur",
  "Width of the table": "Largeur du tableau",
  "Percent": "Pourcent",
  "Pixels": "Pixels",
  "Em": "Em",
  "Width unit": "Unités de largeur",
  "Fixed width columns": "Colonnes à taille fixe",
  "Positioning of this table": "Position du tableau",
  "Cell spacing:": "Espacement",
  "Space between adjacent cells": "Espace entre les cellules adjacentes",
  "Cell padding:": "Marge interne",
  "Space between content and border in cell": "Espace entre le contenu et la bordure d'une cellule",
  "You must enter a number of rows": "Vous devez entrez le nombre de lignes",
  "You must enter a number of columns": "Vous devez entrer le nombre de colonnes",

  // Insert Image
  "Insert Image": "Insérer une image",
  "Image URL:": "URL image",
  "Enter the image URL here": "Entrer l'url de l'image ici",
  "Preview": "Prévisualiser",
  "Preview the image in a new window": "Prévisualiser l'image dans une nouvelle fenêtre",
  "Alternate text:": "Text alternatif",
  "For browsers that don't support images": "Pour les navigateurs qui ne supportent pas les images",
  "Positioning of this image": "Position de l'image",
  "Image Preview:": "Prévisualisation",
  "You must enter the URL": "Vous devez entrer l'URL"
};<commit_msg>apply patch from ticket #627 (french typo correction)<commit_after>// I18N constants
// LANG: "fr", ENCODING: UTF-8
{
  "Bold": "Gras",
  "Italic": "Italique",
  "Underline": "Souligné",
  "Strikethrough": "Barré",
  "Subscript": "Indice",
  "Superscript": "Exposant",
  "Justify Left": "Aligner à gauche",
  "Justify Center": "Centrer",
  "Justify Right": "Aligner à droite",
  "Justify Full": "Justifier",
  "Ordered List": "Liste numérotée",
  "Bulleted List": "Liste à puces",
  "Decrease Indent": "Diminuer le retrait",
  "Increase Indent": "Augmenter le retrait",
  "Font Color": "Couleur de police",
  "Background Color": "Surlignage",
  "Horizontal Rule": "Ligne horizontale",
  "Insert Web Link": "Insérer un lien",
  "Insert/Modify Image": "Insérer / Modifier une image",
  "Insert Table": "Insérer un tableau",
  "Toggle HTML Source": "Afficher / Masquer code source",
  "Enlarge Editor": "Agrandir l'éditeur",
  "About this editor": "A propos",
  "Help using editor": "Aide",
  "Current style": "Style courant",
  "Undoes your last action": "Annuler la dernière action",
  "Redoes your last action": "Répéter la dernière action",
  "Cut selection": "Couper la sélection",
  "Copy selection": "Copier la sélection",
  "Paste from clipboard": "Coller depuis le presse-papier",
  "Direction left to right": "Direction de gauche à droite",
  "Direction right to left": "Direction de droite à gauche",
  "Remove formatting": "Supprimer mise en forme",
  "Select all": "Tout sélectionner",
  "Print document": "Imprimer document",
  "Clear MSOffice tags": "Supprimer tags MSOffice",
  "Clear Inline Font Specifications": "Supprimer paramètres inline de la police",
  "Split Block": "Séparer les blocs",
  "Toggle Borders": "Afficher / Masquer les bordures",
  "Save as": "Enregistrer sous",
  "Insert/Overwrite": "Insertion / Remplacement",
  "— format —": "— Format —",
  "Heading 1": "Titre 1",
  "Heading 2": "Titre 2",
  "Heading 3": "Titre 3",
  "Heading 4": "Titre 4",
  "Heading 5": "Titre 5",
  "Heading 6": "Titre 6",
  "Normal": "Normal",
  "Address": "Adresse",
  "Formatted": "Formaté",

  //dialogs
  "OK": "OK",
  "Cancel": "Annuler",
  "Path": "Chemin",
  "You are in TEXT MODE.  Use the [<>] button to switch back to WYSIWYG.": "Vous êtes en MODE TEXTE.  Appuyez sur le bouton [<>] pour retourner au mode WYSIWYG.",
  "The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.": "Le bouton Coller ne fonctionne pas sur les navigateurs basés sur Mozilla (pour des raisons de sécurité). Pressez CTRL-V au clavier pour coller directement.",
  "Your Document is not well formed. Check JavaScript console for details.": "Le document est mal formé. Vérifiez la console JavaScript pour plus de détails.",

  "Alignment:": "Alignement",
  "Not set": "Indéfini",
  "Left": "Gauche",
  "Right": "Droite",
  "Texttop": "Texttop",
  "Absmiddle": "Absmiddle",
  "Baseline": "Baseline",
  "Absbottom": "Absbottom",
  "Bottom": "Bas",
  "Middle": "Milieu",
  "Top": "Haut",

  "Layout": "Mise en page",
  "Spacing": "Espacement",
  "Horizontal:": "Horizontal",
  "Horizontal padding": "Marge horizontale interne",
  "Vertical:": "Vertical",
  "Vertical padding": "Marge verticale interne",
  "Border thickness:": "Epaisseur de bordure",
  "Leave empty for no border": "Laisser vide pour pas de bordure",

  //Insert Link
  "Insert/Modify Link": "Insérer / Modifier un lien",
  "None (use implicit)": "Aucune (implicite)",
  "New window (_blank)": "Nouvelle fenêtre (_blank)",
  "Same frame (_self)": "Même frame (_self)",
  "Top frame (_top)": "Frame principale (_top)",
  "Other": "Autre",
  "Target:": "Cible",
  "Title (tooltip):": "Texte alternatif",
  "URL:": "URL:",
  "You must enter the URL where this link points to": "Vous devez entrer l'URL de ce lien",
  "You need to select some text before creating a link": "Vous devez sélectionner du texte avant de créer un lien",

  // Insert Table
  "Insert Table": "Insérer un tableau",
  "Rows:": "Lignes",
  "Number of rows": "Nombre de lignes",
  "Cols:": "Colonnes",
  "Number of columns": "Nombre de colonnes",
  "Width:": "Largeur",
  "Width of the table": "Largeur du tableau",
  "Percent": "Pourcent",
  "Pixels": "Pixels",
  "Em": "Em",
  "Width unit": "Unités de largeur",
  "Fixed width columns": "Colonnes à taille fixe",
  "Positioning of this table": "Position du tableau",
  "Cell spacing:": "Espacement",
  "Space between adjacent cells": "Espace entre les cellules adjacentes",
  "Cell padding:": "Marge interne",
  "Space between content and border in cell": "Espace entre le contenu et la bordure d'une cellule",
  "You must enter a number of rows": "Vous devez entrer le nombre de lignes",
  "You must enter a number of columns": "Vous devez entrer le nombre de colonnes",

  // Insert Image
  "Insert Image": "Insérer une image",
  "Image URL:": "URL image",
  "Enter the image URL here": "Entrer l'URL de l'image ici",
  "Preview": "Prévisualiser",
  "Preview the image in a new window": "Prévisualiser l'image dans une nouvelle fenêtre",
  "Alternate text:": "Texte alternatif",
  "For browsers that don't support images": "Pour les navigateurs qui ne supportent pas les images",
  "Positioning of this image": "Position de l'image",
  "Image Preview:": "Prévisualisation",
  "You must enter the URL": "Vous devez entrer l'URL"
};<|endoftext|>


<commit_before>/**
 * @author Oscar Fonts <oscar.fonts@geomati.co>
 */
define('meteo', ['i18n', 'SensorWidget', 'locale-date', 'jquery', 'bootstrap'], function(i18n, SensorWidget, ld, $) {
    "use strict";

    ld.utc(false);
    ld.locale("es");
    i18n.setLang('ca');

    var defs = {
        service: function() {
            return "https://demo.geomatico.es/52n-sos/service";
            //return "http://172.17.4.37:8080/52n-sos/sos/json";
            //return "/52n-sos/sos/json";
        },
        offering: function(p) {
            return "http://sensors.portdebarcelona.cat/def/weather/offerings#" + p;
        },
        feature: function(p) {
            return "http://sensors.portdebarcelona.cat/def/weather/features#" + p;
        },
        property: function(p) {
            return "http://sensors.portdebarcelona.cat/def/weather/properties#" + p;
        }
    };

    var now = new Date();
    var two_hours_ago = new Date(now.getTime() - 1000 * 60 * 60 * 2);
    var three_hours_ago = new Date(now.getTime() - 1000 * 60 * 60 * 3);
    var back_33_samples = new Date(now.getTime() - 1000 * 60 * 60 * 17);
    var a_day_ago = new Date(now.getTime() - 1000 * 60 * 60 * 24);

    var section = $(".active a").text();

    switch (section) {

        case "Sirena":

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".sirena .compass"));

            new SensorWidget('thermometer', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("32"),
                refresh_interval: 15
            }, document.querySelector(".sirena .meteo-thermometer"));

            new SensorWidget('timechart', {
                title: "Velocitat Vent",
                service: defs.service(),
                offering: defs.offering("10M"),
                features: [defs.feature("02")],
                properties: [defs.property("30M"), defs.property("30")],
                time_start: a_day_ago.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z"
            }, document.querySelector(".sirena .timechart"));

            new SensorWidget('windrose', {
                title: "Rosa vents últimes 3h",
                subtitle: "Sirena, mostres minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("31")],
                time_start: three_hours_ago.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z",
                refresh_interval: 120
            }, document.querySelector(".sirena .windrose"));

            new SensorWidget('table', {
                title: "Taula de dades",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("30M"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("35"), defs.property("36"), defs.property("34")],
                time_start: back_33_samples.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z"
            }, document.querySelector(".sirena .tablex"));

            break;

        case "XMVQA":

            new SensorWidget('compass', {
                title: "Sirena",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".xmvqa .left .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("34"), defs.property("35"), defs.property("36")],
                refresh_interval: 15
            }, document.querySelector(".xmvqa .left .panel-10M"));

            new SensorWidget('timechart', {
                title: "Velocitat Vent",
                service: defs.service(),
                offering: defs.offering("10M"),
                features: [defs.feature("02")],
                properties: [defs.property("30")],
                time_start: two_hours_ago.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z"
            }, document.querySelector(".xmvqa .left .timechart"));

            new SensorWidget('panel', {
                title: "Darrers valors 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("34"), defs.property("35"), defs.property("36")],
                refresh_interval: 120
            }, document.querySelector(".xmvqa .left .panel-30M"));

            var stations = {
                "01": "Dispensari",
                "P4": "Dic Sud",
                "03": "Adossat",
                "P6": "Contradic",
                "P3": "Unitat Mobil",
                "P5": "Dàrsena Sud B",
                "10": "ZAL2"
            };

            for (var station in stations) {

                new SensorWidget('compass', {
                    title: stations[station],
                    service: defs.service(),
                    offering: defs.offering("1M"),
                    feature: defs.feature(station),
                    property: defs.property("31"),
                    refresh_interval: 15
                }, document.querySelector(".xmvqa .x" + station + " .compass"));

                new SensorWidget('panel', {
                    title: "Dades minutals",
                    service: defs.service(),
                    offering: defs.offering("1M"),
                    feature: defs.feature(station),
                    properties: [defs.property("30"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("34"), defs.property("35"), defs.property("36")],
                    refresh_interval: 15
                }, document.querySelector(".xmvqa .x" + station + " .panel"));
            }

            break;

        case "Torre Control":

            new SensorWidget('map', {
                service: defs.service(),
                offering: defs.offering("30M"),
                features: [defs.feature("P4")],
                properties: [],
                permanent_tooltips: true,
                swap_axis: true,
                max_initial_zoom: 12
            }, document.querySelector(".torrecontrol .p4 .map"));

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("P4"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .p4 .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("P4"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .p4 .panel-10M"));

            new SensorWidget('panel', {
                title: "Dades 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("P4"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .p4 .panel-30M"));

            new SensorWidget('map', {
                service: defs.service(),
                offering: defs.offering("30M"),
                features: [defs.feature("02")],
                properties: [],
                permanent_tooltips: true,
                swap_axis: true,
                max_initial_zoom: 12
            }, document.querySelector(".torrecontrol .x02 .map"));

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("31"),
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .x02 .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .x02 .panel-10M"));

            new SensorWidget('panel', {
                title: "Dades 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("02"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .x02 .panel-30M"));

            new SensorWidget('map', {
                service: defs.service(),
                offering: defs.offering("30M"),
                features: [defs.feature("03")],
                properties: [],
                permanent_tooltips: true,
                swap_axis: true,
                max_initial_zoom: 12
            }, document.querySelector(".torrecontrol .x03 .map"));

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("03"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .x03 .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("03"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .x03 .panel-10M"));

            new SensorWidget('panel', {
                title: "Dades 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("03"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .x03 .panel-30M"));

            break;

        case "Data Browser":

            var features = ["01", "02", "03", "10", "P1", "P3", "P4", "P5", "P6", "P7"];
            var featureNames = ["01 - Dispensari", "02 - Sirena", "03 - Adossat", "10 - ZAL2", "P1 - ESCU", "P3 - Unitat Mobil", "P4 - Dic Sud", "P5 - Dàrsena sud B", "P6 - Contradic", "P7 - Torre Control"];
            var offerings = ["1M", "10M", "30M"];
            var offeringNames = ["Minutals", "10 minutals", "30 minutals"];

            var tpl = $('#item-template').html();
            var html = "";
            for (var f in features) {
                var res = tpl.replace(/\{\{id}}/g, features[f]);
                res = res.replace(/\{\{title}}/g, featureNames[f]);
                html += res;
            }
            $(".databrowser").html(html);

            $(".panel-collapse").on('show.bs.collapse', function() {
                $(this).parent().removeClass('panel-default').addClass('panel-primary');
                for (var o in offerings) {
                    var feature = this.id;
                    var offering = offerings[o];
                    var title = offeringNames[o];
                    var element = $(this).find(".x" + offering)[0];
                    if (!element.children.length) {
                        new SensorWidget('panel', {
                            title: title,
                            service: defs.service(),
                            offering: defs.offering(offering),
                            feature: defs.feature(feature),
                            properties: [],
                            refresh_interval: 60
                        }, element);
                    }
                }
            });

            $(".panel-collapse").on('hide.bs.collapse', function() {
                $(this).parent().removeClass('panel-primary').addClass('panel-default');
            });

            $(".panel").hover(
                function() {
                    $(this).removeClass('panel-default').addClass('panel-primary');
                }, function() {
                    if (!$(this).find('.in').length && !$(this).find('.collapsing').length) {
                        $(this).removeClass('panel-primary').addClass('panel-default');
                    }
                }
            );

            break;
    }

});

requirejs(['meteo']);
<commit_msg>Update meteo stations in DataBrowser panel<commit_after>/**
 * @author Oscar Fonts <oscar.fonts@geomati.co>
 */
define('meteo', ['i18n', 'SensorWidget', 'locale-date', 'jquery', 'bootstrap'], function(i18n, SensorWidget, ld, $) {
    "use strict";

    ld.utc(false);
    ld.locale("es");
    i18n.setLang('ca');

    var defs = {
        service: function() {
            return "https://demo.geomatico.es/52n-sos/service";
            //return "http://172.17.4.37:8080/52n-sos/sos/json";
            //return "/52n-sos/sos/json";
        },
        offering: function(p) {
            return "http://sensors.portdebarcelona.cat/def/weather/offerings#" + p;
        },
        feature: function(p) {
            return "http://sensors.portdebarcelona.cat/def/weather/features#" + p;
        },
        property: function(p) {
            return "http://sensors.portdebarcelona.cat/def/weather/properties#" + p;
        }
    };

    var now = new Date();
    var two_hours_ago = new Date(now.getTime() - 1000 * 60 * 60 * 2);
    var three_hours_ago = new Date(now.getTime() - 1000 * 60 * 60 * 3);
    var back_33_samples = new Date(now.getTime() - 1000 * 60 * 60 * 17);
    var a_day_ago = new Date(now.getTime() - 1000 * 60 * 60 * 24);

    var section = $(".active a").text();

    switch (section) {

        case "Sirena":

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".sirena .compass"));

            new SensorWidget('thermometer', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("32"),
                refresh_interval: 15
            }, document.querySelector(".sirena .meteo-thermometer"));

            new SensorWidget('timechart', {
                title: "Velocitat Vent",
                service: defs.service(),
                offering: defs.offering("10M"),
                features: [defs.feature("02")],
                properties: [defs.property("30M"), defs.property("30")],
                time_start: a_day_ago.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z"
            }, document.querySelector(".sirena .timechart"));

            new SensorWidget('windrose', {
                title: "Rosa vents últimes 3h",
                subtitle: "Sirena, mostres minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("31")],
                time_start: three_hours_ago.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z",
                refresh_interval: 120
            }, document.querySelector(".sirena .windrose"));

            new SensorWidget('table', {
                title: "Taula de dades",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("30M"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("35"), defs.property("36"), defs.property("34")],
                time_start: back_33_samples.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z"
            }, document.querySelector(".sirena .tablex"));

            break;

        case "XMVQA":

            new SensorWidget('compass', {
                title: "Sirena",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".xmvqa .left .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("34"), defs.property("35"), defs.property("36")],
                refresh_interval: 15
            }, document.querySelector(".xmvqa .left .panel-10M"));

            new SensorWidget('timechart', {
                title: "Velocitat Vent",
                service: defs.service(),
                offering: defs.offering("10M"),
                features: [defs.feature("02")],
                properties: [defs.property("30")],
                time_start: two_hours_ago.toISOString().substring(0, 19) + "Z",
                time_end: now.toISOString().substring(0, 19) + "Z"
            }, document.querySelector(".xmvqa .left .timechart"));

            new SensorWidget('panel', {
                title: "Darrers valors 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("02"),
                properties: [defs.property("30"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("34"), defs.property("35"), defs.property("36")],
                refresh_interval: 120
            }, document.querySelector(".xmvqa .left .panel-30M"));

            var stations = {
                "01": "Dispensari",
                "P4": "Dic Sud",
                "03": "Adossat",
                "P6": "Contradic",
                "P3": "Unitat Mobil",
                "P5": "Dàrsena Sud B",
                "10": "ZAL2"
            };

            for (var station in stations) {

                new SensorWidget('compass', {
                    title: stations[station],
                    service: defs.service(),
                    offering: defs.offering("1M"),
                    feature: defs.feature(station),
                    property: defs.property("31"),
                    refresh_interval: 15
                }, document.querySelector(".xmvqa .x" + station + " .compass"));

                new SensorWidget('panel', {
                    title: "Dades minutals",
                    service: defs.service(),
                    offering: defs.offering("1M"),
                    feature: defs.feature(station),
                    properties: [defs.property("30"), defs.property("31"), defs.property("32"), defs.property("33"), defs.property("34"), defs.property("35"), defs.property("36")],
                    refresh_interval: 15
                }, document.querySelector(".xmvqa .x" + station + " .panel"));
            }

            break;

        case "Torre Control":

            new SensorWidget('map', {
                service: defs.service(),
                offering: defs.offering("30M"),
                features: [defs.feature("P4")],
                properties: [],
                permanent_tooltips: true,
                swap_axis: true,
                max_initial_zoom: 12
            }, document.querySelector(".torrecontrol .p4 .map"));

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("P4"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .p4 .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("P4"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .p4 .panel-10M"));

            new SensorWidget('panel', {
                title: "Dades 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("P4"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .p4 .panel-30M"));

            new SensorWidget('map', {
                service: defs.service(),
                offering: defs.offering("30M"),
                features: [defs.feature("02")],
                properties: [],
                permanent_tooltips: true,
                swap_axis: true,
                max_initial_zoom: 12
            }, document.querySelector(".torrecontrol .x02 .map"));

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                property: defs.property("31"),
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .x02 .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("02"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .x02 .panel-10M"));

            new SensorWidget('panel', {
                title: "Dades 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("02"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .x02 .panel-30M"));

            new SensorWidget('map', {
                service: defs.service(),
                offering: defs.offering("30M"),
                features: [defs.feature("03")],
                properties: [],
                permanent_tooltips: true,
                swap_axis: true,
                max_initial_zoom: 12
            }, document.querySelector(".torrecontrol .x03 .map"));

            new SensorWidget('compass', {
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("03"),
                property: defs.property("31"),
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .x03 .compass"));

            new SensorWidget('panel', {
                title: "Dades minutals",
                service: defs.service(),
                offering: defs.offering("1M"),
                feature: defs.feature("03"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 15
            }, document.querySelector(".torrecontrol .x03 .panel-10M"));

            new SensorWidget('panel', {
                title: "Dades 30-minutals",
                service: defs.service(),
                offering: defs.offering("30M"),
                feature: defs.feature("03"),
                properties: [defs.property("31"), defs.property("30")],
                refresh_interval: 120
            }, document.querySelector(".torrecontrol .x03 .panel-30M"));

            break;

        case "Data Browser":

            var features = ["01", "02", "03", "10", "11", "12", "P5"];
            var featureNames = ["01 - Dispensari", "02 - Sirena", "03 - Adossat", "10 - ZAL Prat", "11 - Bocana Sud", "12 - BEST", "P5 - Dàrsena Sud"];
            var offerings = ["1M", "10M", "30M"];
            var offeringNames = ["Minutals", "10 minutals", "30 minutals"];

            var tpl = $('#item-template').html();
            var html = "";
            for (var f in features) {
                var res = tpl.replace(/\{\{id}}/g, features[f]);
                res = res.replace(/\{\{title}}/g, featureNames[f]);
                html += res;
            }
            $(".databrowser").html(html);

            $(".panel-collapse").on('show.bs.collapse', function() {
                $(this).parent().removeClass('panel-default').addClass('panel-primary');
                for (var o in offerings) {
                    var feature = this.id;
                    var offering = offerings[o];
                    var title = offeringNames[o];
                    var element = $(this).find(".x" + offering)[0];
                    if (!element.children.length) {
                        new SensorWidget('panel', {
                            title: title,
                            service: defs.service(),
                            offering: defs.offering(offering),
                            feature: defs.feature(feature),
                            properties: [],
                            refresh_interval: 60
                        }, element);
                    }
                }
            });

            $(".panel-collapse").on('hide.bs.collapse', function() {
                $(this).parent().removeClass('panel-primary').addClass('panel-default');
            });

            $(".panel").hover(
                function() {
                    $(this).removeClass('panel-default').addClass('panel-primary');
                }, function() {
                    if (!$(this).find('.in').length && !$(this).find('.collapsing').length) {
                        $(this).removeClass('panel-primary').addClass('panel-default');
                    }
                }
            );

            break;
    }

});

requirejs(['meteo']);
<|endoftext|>


<commit_before>/* global describe, console, require, before, beforeEach, after, afterEach, it */

/** The explorations of mocha's fundimentals series continues...
  *
  * This file explores the handling of promises, maximally leveraging what is built into
  * mocha while minimizing help from other libraries. Turns out mocha-as-promised
  * is not necessary. Seems Mocha version 1.18 is when Promise support was added
  *   http://pascalhertleif.de/artikel/using-promises-more-effectively/
  * Which is why if it() returns Promise, no need for done().
  *
  * And node's build-in assert does just fine for these basic tests.
  *
  * After a bit of research, decided to go with Forbes Lindesay's Promise library:
  *   https://www.npmjs.com/package/promise
  *
  * One can read up on that at https://www.promisejs.org/
  *
  * He also did talks on it:
  *   Forbes Lindesay: Promises and Generators: control flow utopia -- JSConf EU 2013:
  *     https://www.youtube.com/watch?v=qbKWsbJ76-s
  *
  * Install it:
  *   npm install promise --save
  */
describe('promises.js:', function(){
  // Moved all the code into one big file-wide describe to keep JSHint from saying:
  //   Use the function form of "use strict"
  'use strict';

  var assert  = require('assert');
  var http    = require('http');
  var fs      = require('fs');

  // Following Lindesay's style here. I guess the Capital is to imply class-ness? Dunno.
  var Promise = require('promise');

  context('when simply instantiating a Promise', function(){
    var aPromise = null;
    before(function(){
      var threwAsExpected = false;
      try{
        // If no function as first param throws: TypeError: not a function
        //   var aPromise = new Promise();
        aPromise = new Promise();
        }
      catch(e){
        if( e.name === 'TypeError' && e.message === 'not a function')
	  threwAsExpected = true;
        }
      finally{
        if(!threwAsExpected)
          throw new Error("bad Promise instantiation did not throw as expects");
        }
      aPromise = new Promise(function(){});
      });

    it('should be able to instantiate a Promise, if pass in a function', function(){
      assert(aPromise, 'aPromise is truthy');
      });
    });

  context('when insta-resolving a Promise in before()', function(){
    var bPromise = null;
    before(function(){
      bPromise = Promise.resolve('basically, unit');
      });

    /* Notice how there is no need for a done callback in the params of the function passed in.
     * With a simple (i.e. non-Promise) async callback, that function def would need to function(done){}
     * but because it() returns the Promise synchronously, no done() is needed to handle asynch-ery,
     * yet then() will happen asynchronously. Mocha (as of 1.18, I hear) is tight like that.
     *
     * Here's the most elegant solution from 2014-11 (which must be >= mocha 1.18, I guess):
     *   http://stackoverflow.com/a/26572442
     *   1. No done passed into it()'s callback
     *   2. No rejection handler (could suppress errors)
     *   3. Return the Promise
     *   4. I guess mocha will then() and catch() on it and repsond appropriately
     */
    it('should find in then(), the value supplied earlier during resolve()', function(){
      return bPromise.then(function(aValue){
        assert.equal(aValue, 'basically, unit', 'bPromise insta-resolved like unit');
        });
      });
    });

  /** Playing around with catch() and how mocha interacts with that.
    */
  context('when in before(), insta-resolving a Promise and returning it', function(){
    var aPromise = null;
    before(function(){
      aPromise = Promise.resolve('I\'m in a good mood so OK');
      return aPromise;
      });

    it('should pass without a catch()', function(){
      return aPromise
        .then(function(aValue){
          assert.equal(aValue, 'I\'m in a good mood so OK', 'bPromise insta-resolved like unit');
          });
      });

    it('should still pass even if there is a catch()', function(){
      return aPromise
        .then(function(aValue){
          assert.equal(aValue, 'I\'m in a good mood so OK', 'bPromise insta-resolved like unit');
          })
        .catch(function(aBadThing){
	  console.log('=======================================catch()') ;
          });
      });
    });

  /* -------------- Now let's play with rejected Promises ------------ */

  /** Odd test here. Actually testing mocha, not a SUT so need this to
    * fail for meta-test to be successful.
    *
    * This confirms that if before() returns a promise which rejects, then
    * it() will never be called, which is good.
    */
  context('when insta-rejecting a Promise in before() the "before all" hook should error. Right here:', function(){
    var aPromise = null;
    before(function(){
      aPromise = new Promise.reject('_this_is_the_reject_reason_string_');
      return aPromise;
      });

    it('should throw bofore it() is called', function(){
      return aPromise.then(function(aValue){
        assert.fail('', '', 'aPromise should have rejected and mocha should have detected that such that this it() should never have been reached.', '');
        });
      });
    });

  /** Just checking that indeed an _async_ reject in before() will prevent it() from being called */
  context('when setTime() rejects a Promise that was returned from before(), it() should never get called', function(){
    var aPromise = null;
    before(function(){
      aPromise = new Promise(function(resolve, reject){
        setTimeout(function(){reject('Intentionally timed out and rejected');}, 500);
        });
      return aPromise;
      });

    it('should throw before it() gets called', function(){
      return aPromise.then(function(aValue){
        assert.fail('', '', 'aPromise should have rejected in before and this it() should never have been called', '');
        });
      });
    });

  /* If a Promise has a catch() and it rejects then that cathc() is
   * supposed to be given a chance to eat the reject message that
   * would otherwise go to mocha. That catch()/rejectHandler can
   * itself throw; that is one way how to say "derp, I can't handle
   * this".
   */
  context('when insta-rejecting a Promise in it()', function(){
    var aPromise = null;
    before(function(){
      /* Normally I like to do set-up of state in before(), not it().
       * But this is a this test of mocha, rather than a SUT, so
       * cannot do the normal structure because want to test a
       * rejected Promise. As was just shown in previous test, a
       * Promise rejected in before() will prevent it() from ever
       * getting called. So, will reject in it() in this situation.
       */
      });

    it('should fail if there is no catch()', function(){
      aPromise = Promise.reject('Not feeling it');
      return aPromise
        .then(function(aValue){
          assert.fail('', '', 'aPromise should have rejected, so this then() should never have been called', '');
          });
      });

    /* This case is interesting: a catch() will prevent mocha from
     * calling this a failed test, as per the design of Promises. For
     * testing purposes I guess that makes sense and is how Promises
     * are supposed to work. I.e. mocha is probably doing a catch()
     * on the returned Promise. If this catch takes care of the
     * problem then mocha's catch() should not be called.
     */
    it('should pass as this catch() "handles" the rejection', function(){
      aPromise = Promise.reject('I\'m in a bad mood so NO');
      return aPromise
        .then(function(aValue){
          assert.fail('', '', 'aPromise should have rejected in before and this it() should never have been called', '');
          })
        .catch(function(aBadThing){
	  // silently "handle" the rejection
          });
      });

    it('should fail since this catch() throws intentionally', function(){
      aPromise = Promise.reject('I\'m in a bad mood so NO');
      return aPromise
        .then(function(aValue){
          assert.fail('', '', 'aPromise should have rejected; this it() should never have been called', '');
          })
        .catch(function(aBadThing){
	  throw new Error(aBadThing);
          });
      });
    });
  });

<commit_msg>Migrated Promise library to Bluebird, from Lindesay's<commit_after>/* global describe, console, require, before, beforeEach, after, afterEach, it */

/** The explorations of mocha's fundimentals series continues...
  *
  * This file explores the handling of promises, maximally leveraging what is built into
  * mocha while minimizing help from other libraries. Turns out mocha-as-promised
  * is not necessary. Seems Mocha version 1.18 is when Promise support was added
  *   http://pascalhertleif.de/artikel/using-promises-more-effectively/
  * Which is why if it() returns Promise, no need for done().
  *
  * And node's build-in assert does just fine for these basic tests.
  *
  * For Promise library, initially used Forbes Lindesay's Promise library:
  *   https://www.npmjs.com/package/promise
  *   https://www.promisejs.org/
  *   npm install promise --save
  * It worked quite well, but after kicking it around for a while, decided
  * to migrate to Bluebird for it stronger community, the social proof, and performance:
  *   http://jsperf.com/pimp-vs-bluebird-vs-q-vs-rsvp
  * Migration was extremely painless.
  *   1. npm install bluebird
  *   2. changed the require()
  *   3. change error message detected when new Promise() w/ arita=0
  */
describe('promises.js:', function(){
  // Moved all the code into one big file-wide describe to keep JSHint from saying:
  //   Use the function form of "use strict"
  'use strict';

  var assert  = require('assert');
  var http    = require('http');
  var fs      = require('fs');

  // Both Lindesay and Bluebird use this Capitalized naming style. I guess it's to imply class-ness? Dunno.
  var Promise = require('bluebird');

  context('when simply instantiating a Promise', function(){
    var aPromise = null;
    before(function(){
      var threwAsExpected = false;
      try{
        // If no function as first param throws: TypeError: not a function
        //   var aPromise = new Promise();
        aPromise = new Promise();

        }
      catch(e){
        if( e.name === 'TypeError' && e.message.substring(0, 52) === 'the promise constructor requires a resolver function')
	  threwAsExpected = true;
        }
      finally{
        if(!threwAsExpected)
          throw new Error("bad Promise instantiation did not throw as expects");
        }
      aPromise = new Promise(function(){});
      });

    it('should be able to instantiate a Promise, if pass in a function', function(){
      assert(aPromise, 'aPromise is truthy');
      });
    });

  context('when insta-resolving a Promise in before()', function(){
    var bPromise = null;
    before(function(){
      bPromise = Promise.resolve('basically, unit');
      });

    /* Notice how there is no need for a done callback in the params of the function passed in.
     * With a simple (i.e. non-Promise) async callback, that function def would need to function(done){}
     * but because it() returns the Promise synchronously, no done() is needed to handle asynch-ery,
     * yet then() will happen asynchronously. Mocha (as of 1.18, I hear) is tight like that.
     *
     * Here's the most elegant solution from 2014-11 (which must be >= mocha 1.18, I guess):
     *   http://stackoverflow.com/a/26572442
     *   1. No done passed into it()'s callback
     *   2. No rejection handler (could suppress errors)
     *   3. Return the Promise
     *   4. I guess mocha will then() and catch() on it and repsond appropriately
     */
    it('should find in then(), the value supplied earlier during resolve()', function(){
      return bPromise.then(function(aValue){
        assert.equal(aValue, 'basically, unit', 'bPromise insta-resolved like unit');
        });
      });
    });

  /** Playing around with catch() and how mocha interacts with that.
    */
  context('when in before(), insta-resolving a Promise and returning it', function(){
    var aPromise = null;
    before(function(){
      aPromise = Promise.resolve('I\'m in a good mood so OK');
      return aPromise;
      });

    it('should pass without a catch()', function(){
      return aPromise
        .then(function(aValue){
          assert.equal(aValue, 'I\'m in a good mood so OK', 'bPromise insta-resolved like unit');
          });
      });

    it('should still pass even if there is a catch()', function(){
      return aPromise
        .then(function(aValue){
          assert.equal(aValue, 'I\'m in a good mood so OK', 'bPromise insta-resolved like unit');
          })
        .catch(function(aBadThing){
	  console.log('=======================================catch()') ;
          });
      });
    });

  /* -------------- Now let's play with rejected Promises ------------ */

  /** Odd test here. Actually testing mocha, not a SUT so need this to
    * fail for meta-test to be successful.
    *
    * This confirms that if before() returns a promise which rejects, then
    * it() will never be called, which is good.
    */
  context('when insta-rejecting a Promise in before() the "before all" hook should error. Right here:', function(){
    var aPromise = null;
    before(function(){
      aPromise = new Promise.reject('_this_is_the_reject_reason_string_');
      return aPromise;
      });

    it('should throw bofore it() is called', function(){
      return aPromise.then(function(aValue){
        assert.fail('', '', 'aPromise should have rejected and mocha should have detected that such that this it() should never have been reached.', '');
        });
      });
    });

  /** Just checking that indeed an _async_ reject in before() will prevent it() from being called */
  context('when setTime() rejects a Promise that was returned from before(), it() should never get called', function(){
    var aPromise = null;
    before(function(){
      aPromise = new Promise(function(resolve, reject){
        setTimeout(function(){reject('Intentionally timed out and rejected');}, 500);
        });
      return aPromise;
      });

    it('should throw before it() gets called', function(){
      return aPromise.then(function(aValue){
        assert.fail('', '', 'aPromise should have rejected in before and this it() should never have been called', '');
        });
      });
    });

  /* If a Promise has a catch() and it rejects then that cathc() is
   * supposed to be given a chance to eat the reject message that
   * would otherwise go to mocha. That catch()/rejectHandler can
   * itself throw; that is one way how to say "derp, I can't handle
   * this".
   */
  context('when insta-rejecting a Promise in it()', function(){
    var aPromise = null;
    before(function(){
      /* Normally I like to do set-up of state in before(), not it().
       * But this is a this test of mocha, rather than a SUT, so
       * cannot do the normal structure because want to test a
       * rejected Promise. As was just shown in previous test, a
       * Promise rejected in before() will prevent it() from ever
       * getting called. So, will reject in it() in this situation.
       */
      });

    it('should fail if there is no catch()', function(){
      aPromise = Promise.reject('Not feeling it');
      return aPromise
        .then(function(aValue){
          assert.fail('', '', 'aPromise should have rejected, so this then() should never have been called', '');
          });
      });

    /* This case is interesting: a catch() will prevent mocha from
     * calling this a failed test, as per the design of Promises. For
     * testing purposes I guess that makes sense and is how Promises
     * are supposed to work. I.e. mocha is probably doing a catch()
     * on the returned Promise. If this catch takes care of the
     * problem then mocha's catch() should not be called.
     */
    it('should pass as this catch() "handles" the rejection', function(){
      aPromise = Promise.reject('I\'m in a bad mood so NO');
      return aPromise
        .then(function(aValue){
          assert.fail('', '', 'aPromise should have rejected in before and this it() should never have been called', '');
          })
        .catch(function(aBadThing){
	  // silently "handle" the rejection
          });
      });

    it('should fail since this catch() throws intentionally', function(){
      aPromise = Promise.reject('I\'m in a bad mood so NO');
      return aPromise
        .then(function(aValue){
          assert.fail('', '', 'aPromise should have rejected; this it() should never have been called', '');
          })
        .catch(function(aBadThing){
	  throw new Error(aBadThing);
          });
      });
    });
  });

<|endoftext|>


<commit_before>// @flow

import get from 'lodash/get'
import pick from 'lodash/pick'
import defaults from 'lodash/defaults'
import path from 'path'
import mime from 'mime/lite'
import invariant from 'assert'
import { Router } from 'express'

import { getPundle, getWatcher } from 'pundle-core'
import { getChunk, getUniqueHash, type Job, type ImportResolved } from 'pundle-api'

import { getOutputFormats, getChunksAffectedByImports } from './helpers'

type Payload = {
  configFilePath?: string,
  configLoadFile?: boolean,
  directory?: string,
  // ^ Either directory to initialize pundle from or an instance
  config?: Object,
  watchConfig?: Object,

  hmr?: boolean, // enabled by default
  lazy?: boolean,
  // Used for chunk/image loading and HMR
  publicPath: string,
  changedCallback?: (changed: Array<ImportResolved>) => void,
  generatedCallback?: (url: string, contents: string | Buffer) => void,
}
const PUNDLE_OPTIONS = ['configFilePath', 'configLoadFile', 'directory']

async function getPundleDevMiddleware(options: Payload) {
  invariant(typeof options.publicPath === 'string', 'options.publicPath must be a string')
  defaults(options, { hmr: true })

  const router = new Router()

  let { publicPath = '/' } = options
  if (!publicPath.endsWith('/')) {
    publicPath = `${publicPath}/`
  }

  let configEntry = get(options, 'config.entry', []).slice()
  if (options.hmr) {
    configEntry = [require.resolve('./client/hmr-client')].concat(configEntry)
  }
  const configCache = get(options, 'config.cache', {})
  if (configCache) {
    configCache.cacheKey = configCache.cacheKey
      ? `${configCache.cacheKey}-watcher`
      : `watcher-${process.env.NODE_ENV || 'development'}`
  }

  const pundle = await getPundle({
    ...pick(options, PUNDLE_OPTIONS),
    config: {
      ...get(options, 'config', {}),
      entry: configEntry,
      output: {
        formats: await getOutputFormats(pick(options, PUNDLE_OPTIONS), publicPath),
        rootDirectory: '/tmp',
      },

      cache: configCache,
    },
  })

  let firstTime = true
  let generated = null
  const filesChanged: Set<ImportResolved> = new Set()
  const filesChangedHMR: Set<ImportResolved> = new Set()
  const hmrConnectedClients = new Set()
  const urlToContents = {}
  const urlToHMRContents = {}

  async function regenerateUrlCache({ chunks, job }) {
    const { outputs } = await pundle.generate(job, chunks)

    function setToUrlContents(filePath: string, contents: string | Buffer) {
      urlToContents[filePath] = contents
      if (options.generatedCallback) {
        options.generatedCallback(filePath, contents)
      }
    }

    outputs.forEach(({ filePath, contents, sourceMap }) => {
      if (filePath) {
        setToUrlContents(filePath, contents)
        if (sourceMap && sourceMap.filePath) {
          setToUrlContents(sourceMap.filePath, sourceMap.contents)
        }
      }
    })
  }
  async function generateForHMR({ job }: { job: Job }) {
    if (!(filesChangedHMR.size && options.hmr && hmrConnectedClients.size)) {
      return
    }

    function setToHMRUrlContents(filePath: string, contents: string | Buffer) {
      urlToHMRContents[filePath] = contents
      if (options.generatedCallback) {
        options.generatedCallback(filePath, contents)
      }
    }

    const transformedJob = await pundle.transformJob(job)
    const changed = Array.from(filesChangedHMR)
    filesChangedHMR.clear()

    const hmrId = Date.now()
    const hmrChunksByFormat = {}

    changed.forEach(fileImport => {
      if (fileImport.format !== 'js') return
      if (!hmrChunksByFormat[fileImport.format]) {
        hmrChunksByFormat[fileImport.format] = getChunk(fileImport.format, `hmr-${hmrId}`)
      }
      hmrChunksByFormat[fileImport.format].imports.push(fileImport)
    })
    const hmrChunks: $FlowFixMe = Object.values(hmrChunksByFormat)
    const { outputs } = await pundle.generate(transformedJob, hmrChunks)
    outputs.forEach(({ filePath, contents, sourceMap }) => {
      if (filePath) {
        setToHMRUrlContents(filePath, contents)
        if (sourceMap && sourceMap.filePath) {
          setToHMRUrlContents(sourceMap.filePath, sourceMap.contents)
        }
      }
    })
    const clientInfo = {
      type: 'update',
      paths: outputs.map(item => ({ url: item.filePath, format: item.format })),
      changedFiles: changed,
      changedModules: changed.map(item => getUniqueHash(item)),
    }
    hmrConnectedClients.forEach(client => {
      client.write(`${JSON.stringify(clientInfo)}`)
    })
    console.log(
      `  [HMR] Writing ${outputs.length} chunk${outputs.length > 1 ? 's' : ''} to ${hmrConnectedClients.size} clients`,
    )

    // Remove HMR contents from memory after 60 seconds
    setTimeout(() => {
      outputs.forEach(({ filePath }) => {
        if (filePath) {
          urlToHMRContents[filePath] = null
        }
      })
    }, 60 * 1000)
  }

  async function generateJobAsync({ job, changed }) {
    const transformedJob = await pundle.transformJob(job)
    const chunks = Array.from(transformedJob.chunks.values())
    if (firstTime) {
      firstTime = false
      await regenerateUrlCache({ job: transformedJob, chunks })
      return
    }
    const chunksToRegenerate = getChunksAffectedByImports(job, chunks, changed)

    if (chunksToRegenerate.length) {
      await regenerateUrlCache({ job: transformedJob, chunks: chunksToRegenerate })
    }
  }

  function generateJob({ job }) {
    if (!generated) {
      generated = generateJobAsync({ job, changed: Array.from(filesChanged.values()) })
      filesChanged.clear()
    }
    return generated
  }

  const { queue, job, initialCompile } = await getWatcher({
    ...(options.watchConfig || {}),
    pundle,
    async generate({ changed }) {
      changed.forEach(fileImport => {
        filesChanged.add(fileImport)
      })
      generated = null
      await generateForHMR({ job })
      if (options.changedCallback) {
        options.changedCallback(changed)
      }
    },
    tick({ newFile }) {
      if (options.hmr && !firstTime) {
        filesChangedHMR.add({ format: newFile.format, filePath: newFile.filePath })
      }
    },
  })

  try {
    if (!options.lazy) {
      await initialCompile()
    }
  } catch (_) {
    // Pre-compile if you can, otherwise move on
    // If there's an error, it'll be caught/shown to user on request
  }

  function asyncRoute(callback: (req: Object, res: Object, next: Function) => Promise<void>) {
    return function(req, res, next) {
      callback(req, res, next).catch(error => {
        pundle.report(error)
        next(error)
      })
    }
  }

  router.get(
    `${publicPath}*`,
    asyncRoute(async function(req, res, next) {
      await initialCompile()

      if (req.url.endsWith('.pundle.hmr')) {
        res.write(JSON.stringify({ type: 'status', enabled: !!options.hmr }))
        if (!options.hmr) {
          res.end()
          return
        }
        hmrConnectedClients.add(res)
        // 24 hours
        req.setTimeout(1000 * 60 * 60 * 24)
        res.on('close', function() {
          hmrConnectedClients.delete(res)
        })
        return
      }

      let { url } = req
      if (url.endsWith('/')) {
        url = `${url}index.html`
      }

      function respondWith(output) {
        const mimeType = mime.getType(path.extname(url)) || 'application/octet-stream'
        res.set('content-type', mimeType)
        res.end(output)
      }

      const hmrContents = urlToHMRContents[url]
      if (hmrContents) {
        respondWith(hmrContents)
        return
      }

      await queue.waitTillIdle()
      await generateJob({ job })

      const contents = urlToContents[url]
      if (contents) {
        respondWith(contents)
        return
      }
      next()
    }),
  )

  return router
}

module.exports = { getPundleDevMiddleware, getChunksAffectedByImports }
<commit_msg>:bug: Re-use same cache for watcher<commit_after>// @flow

import get from 'lodash/get'
import pick from 'lodash/pick'
import defaults from 'lodash/defaults'
import path from 'path'
import mime from 'mime/lite'
import invariant from 'assert'
import { Router } from 'express'

import { getPundle, getWatcher } from 'pundle-core'
import { getChunk, getUniqueHash, type Job, type ImportResolved } from 'pundle-api'

import { getOutputFormats, getChunksAffectedByImports } from './helpers'

type Payload = {
  configFilePath?: string,
  configLoadFile?: boolean,
  directory?: string,
  // ^ Either directory to initialize pundle from or an instance
  config?: Object,
  watchConfig?: Object,

  hmr?: boolean, // enabled by default
  lazy?: boolean,
  // Used for chunk/image loading and HMR
  publicPath: string,
  changedCallback?: (changed: Array<ImportResolved>) => void,
  generatedCallback?: (url: string, contents: string | Buffer) => void,
}
const PUNDLE_OPTIONS = ['configFilePath', 'configLoadFile', 'directory']

async function getPundleDevMiddleware(options: Payload) {
  invariant(typeof options.publicPath === 'string', 'options.publicPath must be a string')
  defaults(options, { hmr: true })

  const router = new Router()

  let { publicPath = '/' } = options
  if (!publicPath.endsWith('/')) {
    publicPath = `${publicPath}/`
  }

  let configEntry = get(options, 'config.entry', []).slice()
  if (options.hmr) {
    configEntry = [require.resolve('./client/hmr-client')].concat(configEntry)
  }

  const pundle = await getPundle({
    ...pick(options, PUNDLE_OPTIONS),
    config: {
      ...get(options, 'config', {}),
      entry: configEntry,
      output: {
        formats: await getOutputFormats(pick(options, PUNDLE_OPTIONS), publicPath),
        rootDirectory: '/tmp',
      },
    },
  })

  let firstTime = true
  let generated = null
  const filesChanged: Set<ImportResolved> = new Set()
  const filesChangedHMR: Set<ImportResolved> = new Set()
  const hmrConnectedClients = new Set()
  const urlToContents = {}
  const urlToHMRContents = {}

  async function regenerateUrlCache({ chunks, job }) {
    const { outputs } = await pundle.generate(job, chunks)

    function setToUrlContents(filePath: string, contents: string | Buffer) {
      urlToContents[filePath] = contents
      if (options.generatedCallback) {
        options.generatedCallback(filePath, contents)
      }
    }

    outputs.forEach(({ filePath, contents, sourceMap }) => {
      if (filePath) {
        setToUrlContents(filePath, contents)
        if (sourceMap && sourceMap.filePath) {
          setToUrlContents(sourceMap.filePath, sourceMap.contents)
        }
      }
    })
  }
  async function generateForHMR({ job }: { job: Job }) {
    if (!(filesChangedHMR.size && options.hmr && hmrConnectedClients.size)) {
      return
    }

    function setToHMRUrlContents(filePath: string, contents: string | Buffer) {
      urlToHMRContents[filePath] = contents
      if (options.generatedCallback) {
        options.generatedCallback(filePath, contents)
      }
    }

    const transformedJob = await pundle.transformJob(job)
    const changed = Array.from(filesChangedHMR)
    filesChangedHMR.clear()

    const hmrId = Date.now()
    const hmrChunksByFormat = {}

    changed.forEach(fileImport => {
      if (fileImport.format !== 'js') return
      if (!hmrChunksByFormat[fileImport.format]) {
        hmrChunksByFormat[fileImport.format] = getChunk(fileImport.format, `hmr-${hmrId}`)
      }
      hmrChunksByFormat[fileImport.format].imports.push(fileImport)
    })
    const hmrChunks: $FlowFixMe = Object.values(hmrChunksByFormat)
    const { outputs } = await pundle.generate(transformedJob, hmrChunks)
    outputs.forEach(({ filePath, contents, sourceMap }) => {
      if (filePath) {
        setToHMRUrlContents(filePath, contents)
        if (sourceMap && sourceMap.filePath) {
          setToHMRUrlContents(sourceMap.filePath, sourceMap.contents)
        }
      }
    })
    const clientInfo = {
      type: 'update',
      paths: outputs.map(item => ({ url: item.filePath, format: item.format })),
      changedFiles: changed,
      changedModules: changed.map(item => getUniqueHash(item)),
    }
    hmrConnectedClients.forEach(client => {
      client.write(`${JSON.stringify(clientInfo)}`)
    })
    console.log(
      `  [HMR] Writing ${outputs.length} chunk${outputs.length > 1 ? 's' : ''} to ${hmrConnectedClients.size} clients`,
    )

    // Remove HMR contents from memory after 60 seconds
    setTimeout(() => {
      outputs.forEach(({ filePath }) => {
        if (filePath) {
          urlToHMRContents[filePath] = null
        }
      })
    }, 60 * 1000)
  }

  async function generateJobAsync({ job, changed }) {
    const transformedJob = await pundle.transformJob(job)
    const chunks = Array.from(transformedJob.chunks.values())
    if (firstTime) {
      firstTime = false
      await regenerateUrlCache({ job: transformedJob, chunks })
      return
    }
    const chunksToRegenerate = getChunksAffectedByImports(job, chunks, changed)

    if (chunksToRegenerate.length) {
      await regenerateUrlCache({ job: transformedJob, chunks: chunksToRegenerate })
    }
  }

  function generateJob({ job }) {
    if (!generated) {
      generated = generateJobAsync({ job, changed: Array.from(filesChanged.values()) })
      filesChanged.clear()
    }
    return generated
  }

  const { queue, job, initialCompile } = await getWatcher({
    ...(options.watchConfig || {}),
    pundle,
    async generate({ changed }) {
      changed.forEach(fileImport => {
        filesChanged.add(fileImport)
      })
      generated = null
      await generateForHMR({ job })
      if (options.changedCallback) {
        options.changedCallback(changed)
      }
    },
    tick({ newFile }) {
      if (options.hmr && !firstTime) {
        filesChangedHMR.add({ format: newFile.format, filePath: newFile.filePath })
      }
    },
  })

  try {
    if (!options.lazy) {
      await initialCompile()
    }
  } catch (_) {
    // Pre-compile if you can, otherwise move on
    // If there's an error, it'll be caught/shown to user on request
  }

  function asyncRoute(callback: (req: Object, res: Object, next: Function) => Promise<void>) {
    return function(req, res, next) {
      callback(req, res, next).catch(error => {
        pundle.report(error)
        next(error)
      })
    }
  }

  router.get(
    `${publicPath}*`,
    asyncRoute(async function(req, res, next) {
      await initialCompile()

      if (req.url.endsWith('.pundle.hmr')) {
        res.write(JSON.stringify({ type: 'status', enabled: !!options.hmr }))
        if (!options.hmr) {
          res.end()
          return
        }
        hmrConnectedClients.add(res)
        // 24 hours
        req.setTimeout(1000 * 60 * 60 * 24)
        res.on('close', function() {
          hmrConnectedClients.delete(res)
        })
        return
      }

      let { url } = req
      if (url.endsWith('/')) {
        url = `${url}index.html`
      }

      function respondWith(output) {
        const mimeType = mime.getType(path.extname(url)) || 'application/octet-stream'
        res.set('content-type', mimeType)
        res.end(output)
      }

      const hmrContents = urlToHMRContents[url]
      if (hmrContents) {
        respondWith(hmrContents)
        return
      }

      await queue.waitTillIdle()
      await generateJob({ job })

      const contents = urlToContents[url]
      if (contents) {
        respondWith(contents)
        return
      }
      next()
    }),
  )

  return router
}

module.exports = { getPundleDevMiddleware, getChunksAffectedByImports }
<|endoftext|>


<commit_before>var util = require('../src/util/seeds');

exports.seed = function(knex, Promise) {
  return util.removeIfExists(knex, 'action_types', {
    id: 1,
    code: 'BUTTON_PUSH',
    name: 'Don\'t Press',
    value: 1,
    cooldown: 30 * 1000
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 2,
      code: 'SIMA',
      name: 'Grab a sima',
      value: 5,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.removeIfExists(knex, 'action_types', {
      id: 4,
      code: 'LECTURE',
      name: 'At a lecture',
      value: 200,
      cooldown: 45 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 5,
      code: 'IMAGE',
      name: 'Pics or didn\'t happen',
      value: 100,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 6,
      code: 'TEXT',
      name: 'Comment',
      value: 10,
      cooldown: 10 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 7,
      code: 'IMAGE_REWARD',
      name: 'Reward',
      value: 200000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 8,
      code: 'IMAGE_REWARD2',
      name: 'Reward 2',
      value: 300000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 9,
      code: 'CHECK_IN_EVENT',
      name: 'Check in',
      value: 50,
      cooldown: 1, // 0 evals unintentionally to false in throttle-core
    });
  });
};
<commit_msg>Increased score awarded for checking into events<commit_after>var util = require('../src/util/seeds');

exports.seed = function(knex, Promise) {
  return util.removeIfExists(knex, 'action_types', {
    id: 1,
    code: 'BUTTON_PUSH',
    name: 'Don\'t Press',
    value: 1,
    cooldown: 30 * 1000
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 2,
      code: 'SIMA',
      name: 'Grab a sima',
      value: 5,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.removeIfExists(knex, 'action_types', {
      id: 4,
      code: 'LECTURE',
      name: 'At a lecture',
      value: 200,
      cooldown: 45 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 5,
      code: 'IMAGE',
      name: 'Pics or didn\'t happen',
      value: 100,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 6,
      code: 'TEXT',
      name: 'Comment',
      value: 10,
      cooldown: 10 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 7,
      code: 'IMAGE_REWARD',
      name: 'Reward',
      value: 200000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 8,
      code: 'IMAGE_REWARD2',
      name: 'Reward 2',
      value: 300000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 9,
      code: 'CHECK_IN_EVENT',
      name: 'Check in',
      value: 200,
      cooldown: 1, // 0 evals unintentionally to false in throttle-core
    });
  });
};
<|endoftext|>


<commit_before>var util = require('../src/util/seeds');

exports.seed = function(knex, Promise) {
  return util.removeIfExists(knex, 'action_types', {
    id: 1,
    code: 'BUTTON_PUSH',
    name: 'Don\'t Press',
    value: 1,
    cooldown: 30 * 1000
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 2,
      code: 'SIMA',
      name: 'Grab a sima',
      value: 5,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.removeIfExists(knex, 'action_types', {
      id: 4,
      code: 'LECTURE',
      name: 'At a lecture',
      value: 200,
      cooldown: 45 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 5,
      code: 'IMAGE',
      name: 'Pics or didn\'t happen',
      value: 100,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 6,
      code: 'TEXT',
      name: 'Comment',
      value: 10,
      cooldown: 10 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 7,
      code: 'IMAGE_REWARD',
      name: 'Reward',
      value: 200000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 8,
      code: 'IMAGE_REWARD2',
      name: 'Reward 2',
      value: 300000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 9,
      code: 'CHECK_IN_EVENT',
      name: 'Check in',
      value: 200,
      cooldown: 1, // 0 evals unintentionally to false in throttle-core
    });
  });
};
<commit_msg>Set cooldown to 0 for check in<commit_after>var util = require('../src/util/seeds');

exports.seed = function(knex, Promise) {
  return util.removeIfExists(knex, 'action_types', {
    id: 1,
    code: 'BUTTON_PUSH',
    name: 'Don\'t Press',
    value: 1,
    cooldown: 30 * 1000
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 2,
      code: 'SIMA',
      name: 'Grab a sima',
      value: 5,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.removeIfExists(knex, 'action_types', {
      id: 4,
      code: 'LECTURE',
      name: 'At a lecture',
      value: 200,
      cooldown: 45 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 5,
      code: 'IMAGE',
      name: 'Pics or didn\'t happen',
      value: 100,
      cooldown: 5 * 60 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 6,
      code: 'TEXT',
      name: 'Comment',
      value: 10,
      cooldown: 10 * 1000
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 7,
      code: 'IMAGE_REWARD',
      name: 'Reward',
      value: 200000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 8,
      code: 'IMAGE_REWARD2',
      name: 'Reward 2',
      value: 300000,
      cooldown: 1000 * 60 * 1000,
      is_user_action: false
    });
  })
  .then(() => {
    return util.insertOrUpdate(knex, 'action_types', {
      id: 9,
      code: 'CHECK_IN_EVENT',
      name: 'Check in',
      value: 200,
      cooldown: 0,
    });
  });
};
<|endoftext|>