<commit_before>/**
@license
Copyright 2017 The FOAM Authors. All Rights Reserved.
http://www.apache.org/licenses/LICENSE-2.0
*/

foam.CLASS({
  refines: 'foam.core.Property',

  properties: [
    {
      class: 'Boolean',
      name: 'xmlAttribute',
      default: false
    },
    {
      name: 'fromXML',
      value: function fromXML(value, ctx, prop, xml) {
        return foam.xml.parse(value, null, ctx);
      }
    },
    {
      name: 'toXML',
      value: function toXML(value, Outputter) { return value; }
    }
  ],

  methods: [
    function outputXML(o) {
      o.output({ class: '__Property__', forClass_: this.forClass_ });
    }
  ]
});

/** Add toXML() method to FObject. **/
foam.CLASS({
  refines: 'foam.core.FObject',

  methods: [
    /**
      Output as a pretty-printed XML-ish String.
      Use for debugging/testing purposes. If you want actual
      XML output, use foam.xml.* instead.
    */
    function stringify() {
      return foam.xml.Pretty.stringify(this);
    }
  ]
});

/** XML Outputter **/
foam.CLASS({
  package: 'foam.xml',
  name: "Outputter",

  documentation: 'XML Outputter.',

  properties: [
    {
      class: 'String',
      name: 'buf_',
      value: ''
    },
    {
      class: 'Int',
      name: 'indentLevel_',
      value: 0
    },
    {
      class: 'String',
      name: 'indentStr',
      value: '\t'
    },
    {
      class: 'String',
      name: 'nlStr',
      value: '\n'
    },
    {
      class: 'Boolean',
      name: 'outputDefaultValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'outputDefinedValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'formatDatesAsNumbers',
      value: false
    },
    {
      class: 'Boolean',
      name: 'outputClassNames',
      value: true
    },
    {
      class: 'Function',
      name: 'propertyPredicate',
      value: function(o, p) { return ! p.transient; }
    },
    {
      class: 'Boolean',
      name: 'useShortNames',
      value: false
    },
        {
      class: 'Boolean',
      name: 'sortObjectKeys',
      value: false
    },
    {
      class: 'Boolean',
      name: 'pretty',
      value: true,
      postSet: function(_, p) {
        if ( p ) {
          this.clearProperty('indentStr');
          this.clearProperty('nlStr');
          this.clearProperty('useShortNames');
        } else {
          this.indentStr = this.nlStr = null;
        }
      }
    }
  ],

  methods: [

    function reset() {
      this.indentLevel_ = 0;
      this.buf_ = '';
      return this;
    },

    function escape(str) {
        return str && str.toString()
            .replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>');
    },

    function maybeEscapeKey(str) {
      return this.alwaysQuoteKeys || ! /^[a-zA-Z\$_][0-9a-zA-Z$_]*$/.test(str) ?
          '"' + str + '"' :
          str ;
    },

    function escapeAttr(str) {
        return str && str.replace(/"/g, '"');
    },

    function out() {
      for ( var i = 0 ; i < arguments.length ; i++ ) this.buf_ += arguments[i];
      return this;
    },

    function start(c) {
      if ( c ) this.out(c);
      if ( this.indentStr ) {
        this.indentLevel_++;
        this.indent();
      }
    },

    function end(c) {
      if ( this.indent ) {
        this.indentLevel_--;
      }
      if ( c ) this.nl().indent().out(c);
      return this;
    },

    function nl() {
      if ( this.nlStr && this.nlStr.length ) {
        this.out(this.nlStr);
      }
      return this;
    },

    function indent() {
      for ( var i = 0 ; i < this.indentLevel_ ; i++ ) this.out(this.indentStr);
      return this;
    },

    function outputPropertyName(p) {
      this.out(this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name));
      return this;
    },

    function outputAttributes(v) {
      var attributes = v.cls_.getAxiomsByClass(foam.core.Property).filter(function (p) { return p.xmlAttribute });
      if ( attributes.length === 0 ) return this;

      for ( var i = 0 ; i < attributes.length ; i++ ) {
        this.out(' ' + attributes[i].name + '="' + this.escapeAttr(attributes[i].get(v)) + '"');
      }
      return this;
    },

    function propertyName(p) {
      return this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name)
    },

    function outputProperty_(o, p) {
      if ( ! this.propertyPredicate(o, p ) ) return;
      // don't output default values unless value is defined and outputDefinedValues set to true
      if ( ( ! this.outputDefaultValues && p.isDefaultValue(o[p.name]) ) &&
        ( this.outputDefinedValues && ! o.hasOwnProperty(p.name) ) ) {
        return;
      }

      var v = o[p.name];
      if ( ! v || ( v instanceof Array && v.length === 0 ) ) {
        return;
      }

      this.nl().indent();
      this.outputProperty(v, p);
    },

    {
      name: 'outputProperty',
      code: foam.mmethod({
        Undefined:    function(v, p) {},
        String:       function(v, p) { this.outputPrimitive(v, p); },
        Number:       function(v, p) { this.outputPrimitive(v, p); },
        Boolean:      function(v, p) { this.outputPrimitive(v, p); },
        Date:         function(v, p) { this.outputPrimitive(v, p); },
        AbstractEnum: function(v, p) { this.outputPrimitive(v.name, p); },
        Array:     function(v, p) {
          for ( var i = 0 ; i < v.length ; i++ ) {
            if ( foam.core.FObjectArray.isInstance(p) ) {
              // output FObject array
              this.start('<' + this.propertyName(p) + '>');
              this.output(p.toXML(v[i], this));
              this.end('</' + this.propertyName(p) + '>');
            } else {
              // output primitive array
              this.outputPrimitive(v[i], p);
            }

            // new line and indent except on last element
            if ( i != v.length - 1 ) this.nl().indent();
          }
        },
        FObject: function(v, p) {
          if ( v.xmlValue ) {
            // if v.xmlValue exists then we have attributes
            // check if the value is an FObject and structure XML accordingly
            if ( foam.core.FObject.isInstance(v.xmlValue) ) {
              this.start('<' + this.propertyName(p) + this.outputAttributes(v) + '>');
              this.output(p.toXML(v, this));
              this.end('</' +  this.propertyName(p) + '>');
            } else {
              this.out('<').outputPropertyName(p).outputAttributes(v).out('>');
              this.out(p.toXML(v.xmlValue, this));
              this.out('</').outputPropertyName(p).out('>');
            }
          } else {
            // assume no attributes
            this.start('<' + this.propertyName(p) + '>');
            this.output(p.toXML(v, this));
            this.end('</' +  this.propertyName(p) + '>');
          }
        }
      })
    },

    function outputPrimitive(v, p){
      this.out('<').outputPropertyName(p).out('>');
      this.output(p.toXML(v, this));
      this.out('</').outputPropertyName(p).out('>');
    },

    function outputDate(o) {
      if ( this.formatDatesAsNumbers ) {
        this.out(o.valueOf());
      } else {
        this.out(o.toISOString());
      }
    },

    function outputFunction(o) {
      if ( this.formatFunctionsAsStrings ) {
        this.output(o.toString());
      } else {
        this.out(o.toString());
      }
    },

    function outputObjectKeyValue_(key, value, first) {
      if ( ! first ) this.out(',').nl().indent();
      this.out(this.maybeEscapeKey(key), ':').output(value);
    },

    function outputObjectKeyValues_(o) {
      var first = true;
      for ( var key in o ) {
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    function outputSortedObjectKeyValues_(o) {
      var key, keys = [];

      for ( key in o ) keys.push(key);
      keys.sort();

      var first = true;
      for ( var i = 0 ; i < keys.length; i++ ) {
        key = keys[i];
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    {
      name: 'output',
      code: foam.mmethod({
        Undefined:    function(o) { this.out('null'); },
        Null:         function(o) { this.out('null'); },
        String:       function(o) { this.out(this.escape(o)); },
        Number:       function(o) { this.out(o); },
        Boolean:      function(o) { this.out(o); },
        Date:         function(o) { this.outputDate(o); },
        Function:     function(o) { this.outputFunction(o); },
        AbstractEnum: function(o) { },
        FObject:      function(o) {
          if ( o.outputXML ) {
            o.outputXML(this)
            return;
          }

          var clsName = o.cls_.id;
          // Iterate through properties and output
          var ps = o.cls_.getAxiomsByClass(foam.core.Property);
          for ( var i = 0 ; i < ps.length ; i++ ) {
            // skip outputting of attributes
            if ( ps[i].xmlAttribute ) continue;
            this.outputProperty_(o, ps[i]);
          }
        },
        Object: function(o) {
          if ( o.outputXML ) {
            o.outputXML(this);
          } else {
            var oName = o.name;
            this.start("<object name='" + oName + "'>");
            if ( this.sortObjectKeys ) {
              this.outputSortedObjectKeyValues_(o);
            } else {
              this.outputObjectKeyValues_(o);
            }
            this.end('</object>');
          }
        }
      })
    },

    function stringify(o) {
      // Root tags of objects for array of FObjects
      if ( o instanceof Array ) {
        this.start("<objects>");
        this.output(o);
        this.end("</objects>");
      } else {
        this.output(o);
      }
      var ret = this.buf_;
      this.reset(); // reset to avoid retaining garbage
      return ret;
    },

    function objectify(doc, cls) {
      var obj = cls.create();
      var children = doc.children;

      for ( var i = 0 ; i < children.length ; i++ ) {
        // fetch property based on xml tag name since they may not be in order
        var node = children[i];
        var prop = obj.cls_.getAxiomByName(node.tagName);

        if ( foam.core.FObjectProperty.isInstance(prop) ) {
          // parse FObjectProperty
          prop.set(obj, this.objectify(node, prop.of));
        } else if ( foam.core.FObjectArray.isInstance(prop) ) {
          // parse array property
          prop.get(obj).push(this.objectify(node, foam.lookup(prop.of)));

        } else if ( foam.core.StringArray.isInstance(prop) ) {
          // parse string array
          prop.get(obj).push(node.firstChild ? node.firstChild.nodeValue : null);
        } else {
          // parse property
          prop.set(obj, node.firstChild ? node.firstChild.nodeValue : null);
        }
      }

      // check to see if xmlValue property exists
      var xmlValueProp = obj.cls_.getAxiomByName('xmlValue');
      if ( xmlValueProp ) {
        // parse attributes if they exist
        var attributes = doc.attributes;
        for ( var i = 0 ; i < attributes.length ; i++ ) {
          var attribute = attributes[i];
          var prop = obj.cls_.getAxiomByName(attribute.name);
          // don't need to check for types as attributes are always simple types
          prop.set(obj, attribute.value);
        }

        if ( foam.core.FObjectProperty.isInstance(xmlValueProp) ) {
          xmlValueProp.set(obj, this.objectify(doc, xmlValueProp.of));
        } else {
          xmlValueProp.set(obj, doc.firstChild ? doc.firstChild.nodeValue : null);
        }
      }

      return obj;
    },

    function parseString(str, opt_class) {
      // create DOM
      var parser = new DOMParser();
      var doc = parser.parseFromString(str, 'text/xml');
      var root = doc.firstChild;

      var rootClass = root.getAttribute('class');
      if ( rootClass )
        return this.objectify(root, foam.lookup(rootClass));

      if ( opt_class ) {
        // lookup class if given a string
        if ( typeof(opt_class) === 'string' )
          opt_class = foam.lookup(opt_class);
        return this.objectify(root, opt_class);
      }

      throw new Error('Class not provided');
    }
  ]
});

/** Library of pre-configured XML Outputters. **/
foam.LIB({
  name: 'foam.xml',

  constants: {
    // Pretty Print
    Pretty: foam.xml.Outputter.create({
      outputDefaultValues: false,
      outputDefinedValues: true
    }),

    // Compact output (not pretty)
    Compact: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false
    }),

    // Shorter than Compact (uses short-names if available)
    Short: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: true,
      // TODO: No deserialization support for shortnames yet.
      //      useShortNames: true,
      useShortNames: false,
    })
  },

  methods: [
    function stringify(o) {
      return foam.xml.Compact.stringify(o);
    },

    function objectify(o) {
      return foam.xml.Compact.objectify(o);
    }
  ]
});
<commit_msg>Changed outputDefinedValues to false for Short<commit_after>/**
@license
Copyright 2017 The FOAM Authors. All Rights Reserved.
http://www.apache.org/licenses/LICENSE-2.0
*/

foam.CLASS({
  refines: 'foam.core.Property',

  properties: [
    {
      class: 'Boolean',
      name: 'xmlAttribute',
      default: false
    },
    {
      name: 'fromXML',
      value: function fromXML(value, ctx, prop, xml) {
        return foam.xml.parse(value, null, ctx);
      }
    },
    {
      name: 'toXML',
      value: function toXML(value, Outputter) { return value; }
    }
  ],

  methods: [
    function outputXML(o) {
      o.output({ class: '__Property__', forClass_: this.forClass_ });
    }
  ]
});

/** Add toXML() method to FObject. **/
foam.CLASS({
  refines: 'foam.core.FObject',

  methods: [
    /**
      Output as a pretty-printed XML-ish String.
      Use for debugging/testing purposes. If you want actual
      XML output, use foam.xml.* instead.
    */
    function stringify() {
      return foam.xml.Pretty.stringify(this);
    }
  ]
});

/** XML Outputter **/
foam.CLASS({
  package: 'foam.xml',
  name: "Outputter",

  documentation: 'XML Outputter.',

  properties: [
    {
      class: 'String',
      name: 'buf_',
      value: ''
    },
    {
      class: 'Int',
      name: 'indentLevel_',
      value: 0
    },
    {
      class: 'String',
      name: 'indentStr',
      value: '\t'
    },
    {
      class: 'String',
      name: 'nlStr',
      value: '\n'
    },
    {
      class: 'Boolean',
      name: 'outputDefaultValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'outputDefinedValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'formatDatesAsNumbers',
      value: false
    },
    {
      class: 'Boolean',
      name: 'outputClassNames',
      value: true
    },
    {
      class: 'Function',
      name: 'propertyPredicate',
      value: function(o, p) { return ! p.transient; }
    },
    {
      class: 'Boolean',
      name: 'useShortNames',
      value: false
    },
        {
      class: 'Boolean',
      name: 'sortObjectKeys',
      value: false
    },
    {
      class: 'Boolean',
      name: 'pretty',
      value: true,
      postSet: function(_, p) {
        if ( p ) {
          this.clearProperty('indentStr');
          this.clearProperty('nlStr');
          this.clearProperty('useShortNames');
        } else {
          this.indentStr = this.nlStr = null;
        }
      }
    }
  ],

  methods: [

    function reset() {
      this.indentLevel_ = 0;
      this.buf_ = '';
      return this;
    },

    function escape(str) {
        return str && str.toString()
            .replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>');
    },

    function maybeEscapeKey(str) {
      return this.alwaysQuoteKeys || ! /^[a-zA-Z\$_][0-9a-zA-Z$_]*$/.test(str) ?
          '"' + str + '"' :
          str ;
    },

    function escapeAttr(str) {
        return str && str.replace(/"/g, '"');
    },

    function out() {
      for ( var i = 0 ; i < arguments.length ; i++ ) this.buf_ += arguments[i];
      return this;
    },

    function start(c) {
      if ( c ) this.out(c);
      if ( this.indentStr ) {
        this.indentLevel_++;
        this.indent();
      }
    },

    function end(c) {
      if ( this.indent ) {
        this.indentLevel_--;
      }
      if ( c ) this.nl().indent().out(c);
      return this;
    },

    function nl() {
      if ( this.nlStr && this.nlStr.length ) {
        this.out(this.nlStr);
      }
      return this;
    },

    function indent() {
      for ( var i = 0 ; i < this.indentLevel_ ; i++ ) this.out(this.indentStr);
      return this;
    },

    function outputPropertyName(p) {
      this.out(this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name));
      return this;
    },

    function outputAttributes(v) {
      var attributes = v.cls_.getAxiomsByClass(foam.core.Property).filter(function (p) { return p.xmlAttribute });
      if ( attributes.length === 0 ) return this;

      for ( var i = 0 ; i < attributes.length ; i++ ) {
        this.out(' ' + attributes[i].name + '="' + this.escapeAttr(attributes[i].get(v)) + '"');
      }
      return this;
    },

    function propertyName(p) {
      return this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name)
    },

    function outputProperty_(o, p) {
      if ( ! this.propertyPredicate(o, p ) ) return;
      // don't output default values unless value is defined and outputDefinedValues set to true
      if ( ( ! this.outputDefaultValues && p.isDefaultValue(o[p.name]) ) &&
        ( this.outputDefinedValues && ! o.hasOwnProperty(p.name) ) ) {
        return;
      }

      var v = o[p.name];
      if ( ! v || ( v instanceof Array && v.length === 0 ) ) {
        return;
      }

      this.nl().indent();
      this.outputProperty(v, p);
    },

    {
      name: 'outputProperty',
      code: foam.mmethod({
        Undefined:    function(v, p) {},
        String:       function(v, p) { this.outputPrimitive(v, p); },
        Number:       function(v, p) { this.outputPrimitive(v, p); },
        Boolean:      function(v, p) { this.outputPrimitive(v, p); },
        Date:         function(v, p) { this.outputPrimitive(v, p); },
        AbstractEnum: function(v, p) { this.outputPrimitive(v.name, p); },
        Array:     function(v, p) {
          for ( var i = 0 ; i < v.length ; i++ ) {
            if ( foam.core.FObjectArray.isInstance(p) ) {
              // output FObject array
              this.start('<' + this.propertyName(p) + '>');
              this.output(p.toXML(v[i], this));
              this.end('</' + this.propertyName(p) + '>');
            } else {
              // output primitive array
              this.outputPrimitive(v[i], p);
            }

            // new line and indent except on last element
            if ( i != v.length - 1 ) this.nl().indent();
          }
        },
        FObject: function(v, p) {
          if ( v.xmlValue ) {
            // if v.xmlValue exists then we have attributes
            // check if the value is an FObject and structure XML accordingly
            if ( foam.core.FObject.isInstance(v.xmlValue) ) {
              this.start('<' + this.propertyName(p) + this.outputAttributes(v) + '>');
              this.output(p.toXML(v, this));
              this.end('</' +  this.propertyName(p) + '>');
            } else {
              this.out('<').outputPropertyName(p).outputAttributes(v).out('>');
              this.out(p.toXML(v.xmlValue, this));
              this.out('</').outputPropertyName(p).out('>');
            }
          } else {
            // assume no attributes
            this.start('<' + this.propertyName(p) + '>');
            this.output(p.toXML(v, this));
            this.end('</' +  this.propertyName(p) + '>');
          }
        }
      })
    },

    function outputPrimitive(v, p){
      this.out('<').outputPropertyName(p).out('>');
      this.output(p.toXML(v, this));
      this.out('</').outputPropertyName(p).out('>');
    },

    function outputDate(o) {
      if ( this.formatDatesAsNumbers ) {
        this.out(o.valueOf());
      } else {
        this.out(o.toISOString());
      }
    },

    function outputFunction(o) {
      if ( this.formatFunctionsAsStrings ) {
        this.output(o.toString());
      } else {
        this.out(o.toString());
      }
    },

    function outputObjectKeyValue_(key, value, first) {
      if ( ! first ) this.out(',').nl().indent();
      this.out(this.maybeEscapeKey(key), ':').output(value);
    },

    function outputObjectKeyValues_(o) {
      var first = true;
      for ( var key in o ) {
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    function outputSortedObjectKeyValues_(o) {
      var key, keys = [];

      for ( key in o ) keys.push(key);
      keys.sort();

      var first = true;
      for ( var i = 0 ; i < keys.length; i++ ) {
        key = keys[i];
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    {
      name: 'output',
      code: foam.mmethod({
        Undefined:    function(o) { this.out('null'); },
        Null:         function(o) { this.out('null'); },
        String:       function(o) { this.out(this.escape(o)); },
        Number:       function(o) { this.out(o); },
        Boolean:      function(o) { this.out(o); },
        Date:         function(o) { this.outputDate(o); },
        Function:     function(o) { this.outputFunction(o); },
        AbstractEnum: function(o) { },
        FObject:      function(o) {
          if ( o.outputXML ) {
            o.outputXML(this)
            return;
          }

          var clsName = o.cls_.id;
          // Iterate through properties and output
          var ps = o.cls_.getAxiomsByClass(foam.core.Property);
          for ( var i = 0 ; i < ps.length ; i++ ) {
            // skip outputting of attributes
            if ( ps[i].xmlAttribute ) continue;
            this.outputProperty_(o, ps[i]);
          }
        },
        Object: function(o) {
          if ( o.outputXML ) {
            o.outputXML(this);
          } else {
            var oName = o.name;
            this.start("<object name='" + oName + "'>");
            if ( this.sortObjectKeys ) {
              this.outputSortedObjectKeyValues_(o);
            } else {
              this.outputObjectKeyValues_(o);
            }
            this.end('</object>');
          }
        }
      })
    },

    function stringify(o) {
      // Root tags of objects for array of FObjects
      if ( o instanceof Array ) {
        this.start("<objects>");
        this.output(o);
        this.end("</objects>");
      } else {
        this.output(o);
      }
      var ret = this.buf_;
      this.reset(); // reset to avoid retaining garbage
      return ret;
    },

    function objectify(doc, cls) {
      var obj = cls.create();
      var children = doc.children;

      for ( var i = 0 ; i < children.length ; i++ ) {
        // fetch property based on xml tag name since they may not be in order
        var node = children[i];
        var prop = obj.cls_.getAxiomByName(node.tagName);

        if ( foam.core.FObjectProperty.isInstance(prop) ) {
          // parse FObjectProperty
          prop.set(obj, this.objectify(node, prop.of));
        } else if ( foam.core.FObjectArray.isInstance(prop) ) {
          // parse array property
          prop.get(obj).push(this.objectify(node, foam.lookup(prop.of)));

        } else if ( foam.core.StringArray.isInstance(prop) ) {
          // parse string array
          prop.get(obj).push(node.firstChild ? node.firstChild.nodeValue : null);
        } else {
          // parse property
          prop.set(obj, node.firstChild ? node.firstChild.nodeValue : null);
        }
      }

      // check to see if xmlValue property exists
      var xmlValueProp = obj.cls_.getAxiomByName('xmlValue');
      if ( xmlValueProp ) {
        // parse attributes if they exist
        var attributes = doc.attributes;
        for ( var i = 0 ; i < attributes.length ; i++ ) {
          var attribute = attributes[i];
          var prop = obj.cls_.getAxiomByName(attribute.name);
          // don't need to check for types as attributes are always simple types
          prop.set(obj, attribute.value);
        }

        if ( foam.core.FObjectProperty.isInstance(xmlValueProp) ) {
          xmlValueProp.set(obj, this.objectify(doc, xmlValueProp.of));
        } else {
          xmlValueProp.set(obj, doc.firstChild ? doc.firstChild.nodeValue : null);
        }
      }

      return obj;
    },

    function parseString(str, opt_class) {
      // create DOM
      var parser = new DOMParser();
      var doc = parser.parseFromString(str, 'text/xml');
      var root = doc.firstChild;

      var rootClass = root.getAttribute('class');
      if ( rootClass )
        return this.objectify(root, foam.lookup(rootClass));

      if ( opt_class ) {
        // lookup class if given a string
        if ( typeof(opt_class) === 'string' )
          opt_class = foam.lookup(opt_class);
        return this.objectify(root, opt_class);
      }

      throw new Error('Class not provided');
    }
  ]
});

/** Library of pre-configured XML Outputters. **/
foam.LIB({
  name: 'foam.xml',

  constants: {
    // Pretty Print
    Pretty: foam.xml.Outputter.create({
      outputDefaultValues: false,
      outputDefinedValues: true
    }),

    // Compact output (not pretty)
    Compact: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false
    }),

    // Shorter than Compact (uses short-names if available)
    Short: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false,
      // TODO: No deserialization support for shortnames yet.
      //      useShortNames: true,
      useShortNames: false,
    })
  },

  methods: [
    function stringify(o) {
      return foam.xml.Compact.stringify(o);
    },

    function objectify(o) {
      return foam.xml.Compact.objectify(o);
    }
  ]
});
<|endoftext|>


<commit_before>define([
    'jquery',
    'sir-trevor'
], function($, SirTrevor) {

    'use strict';

    // BLOCK: Image with Description and Credit
    var ImageWithDescriptionAndCreditFactory = function (block_name) {
        return SirTrevor.Block.extend({
            type: block_name,
            title: function() {
                return 'image';
            },
            droppable: true,
            uploadable: true,
            icon_name: 'image',
            loadData: function(data) {
                this.$editor.html($('<img>', {
                    src: data.file.url
                })).show();
                this.$editor.append($('<input>', {
                    type: 'text',
                    name: 'description',
                    placeholder: 'Description',
                    value: data.text
                }));
                this.$editor.append($('<input>', {
                    type: 'text',
                    name: 'credit',
                    placeholder: 'Credit',
                    value: data.text
                }));
            },
            getOptions: function() {
                return SirTrevor.getInstance(this.instanceID).options;
            },
            onBlockRender: function() {
                // assert we have an uploader function in options
                if (typeof(this.getOptions().uploader) !== 'function') {
                    throw block_name  + ' need an `uploader` function in options.';
                }
                /* Setup the upload button */
                this.$inputs.find('button').bind('click', function(ev) {
                    ev.preventDefault();
                });
                this.$inputs.find('input').on('change', _.bind(function(ev) {
                    this.onDrop(ev.currentTarget);
                }, this));
            },
            onDrop: function(transferData) {
                var file = transferData.files[0];
                var urlAPI = window.URL;
                if (typeof urlAPI === 'undefined') {
                    urlAPI = window.webkitURL;
                }
                // Handle one upload at a time
                if (/image/.test(file.type)) {
                    this.loading();
                    // Show this image on here
                    this.$inputs.hide();
                    this.loadData({
                        file: {
                            url: urlAPI.createObjectURL(file)
                        }
                    });
                    this.getOptions().uploader(
                        file,
                        function(data) {
                            this.setData(data);
                            this.ready();
                        },
                        function(error) {
                            this.addMessage(window.i18n.t('blocks:image:upload_error'));
                            this.ready();
                        }
                    );
                }
            }
        });
    };
    return ImageWithDescriptionAndCreditFactory;
});

// EOF
<commit_msg>Image upload : text updated<commit_after>define([
    'jquery',
    'sir-trevor'
], function($, SirTrevor) {

    'use strict';

    // BLOCK: Image with Description and Credit
    var ImageWithDescriptionAndCreditFactory = function (block_name) {
        return SirTrevor.Block.extend({
            type: block_name,
            title: function() {
                return 'Add image';
            },
            droppable: true,
            uploadable: true,
            icon_name: 'image',
            loadData: function(data) {
                this.$editor.html($('<img>', {
                    src: data.file.url
                })).show();
                this.$editor.append($('<input>', {
                    type: 'text',
                    name: 'description',
                    placeholder: 'Description',
                    value: data.text
                }));
                this.$editor.append($('<input>', {
                    type: 'text',
                    name: 'credit',
                    placeholder: 'Credit',
                    value: data.text
                }));
            },
            getOptions: function() {
                return SirTrevor.getInstance(this.instanceID).options;
            },
            onBlockRender: function() {
                // assert we have an uploader function in options
                if (typeof(this.getOptions().uploader) !== 'function') {
                    throw block_name  + ' need an `uploader` function in options.';
                }
                /* Setup the upload button */
                this.$inputs.find('button').bind('click', function(ev) {
                    ev.preventDefault();
                });
                this.$inputs.find('input').on('change', _.bind(function(ev) {
                    this.onDrop(ev.currentTarget);
                }, this));
            },
            onDrop: function(transferData) {
                var file = transferData.files[0];
                var urlAPI = window.URL;
                if (typeof urlAPI === 'undefined') {
                    urlAPI = window.webkitURL;
                }
                // Handle one upload at a time
                if (/image/.test(file.type)) {
                    this.loading();
                    // Show this image on here
                    this.$inputs.hide();
                    this.loadData({
                        file: {
                            url: urlAPI.createObjectURL(file)
                        }
                    });
                    this.getOptions().uploader(
                        file,
                        function(data) {
                            this.setData(data);
                            this.ready();
                        },
                        function(error) {
                            this.addMessage(window.i18n.t('blocks:image:upload_error'));
                            this.ready();
                        }
                    );
                }
            }
        });
    };
    return ImageWithDescriptionAndCreditFactory;
});

// EOF
<|endoftext|>


<commit_before>/**
 * httpCache: An implementation of an in memory HTTP cache that persists data to
 * localStorage.
 */
(function(window, Mobify) {
    /**
     * Retrieve `key` from the cache. Mark as used if `increment` is set.
     */
var get = function(key, increment) {
        // Ignore anchors.
        var resource = cache[key.split('#')[0]];

        if (resource && increment) {
            resource.lastUsed = Date.now();
            resource.useCount = resource.useCount++ || 1;
        }

        return resource;
    }

  , set = function(key, val) {
        cache[key] = val;
    }

    /**
     * Load the persistent cache into memory. Ignore stale resources.
     */
  , load = function() {
        var data = localStorage.getItem(localStorageKey)
          , key;

        if (data === null) return;

        try {
            data = JSON.parse(data)
        } catch(err) {
            return;
        }

        for (key in data) {
            if (data.hasOwnProperty(key) && !httpCache.utils.isStale(data[key])) {
                set(key, data[key]);
            }
        }
    }

    /**
     * Save the in-memory cache to disk. If the disk is full, use LRU to drop
     * resources until it will fit on disk.
     */
  , save = function(callback) {
        var resources = {}
          , resource
          , attempts = 10
          , key
          , serialized
            // End of time.
          , lruTime = 9007199254740991
          , lruKey;

        for (key in cache) {
            if (cache.hasOwnProperty(key)) {
                resources[key] = cache[key]
            }
        }

        (function persist() {
            setTimeout(function() {
                try {
                    serialized = JSON.stringify(resources);
                } catch(err) {
                    if (callback) callback(err);
                    return;
                }

                try {
                    localStorage.setItem(localStorageKey, serialized)
                } catch(err) {
                    if (!--attempts) {
                        if (callback) callback(err);
                        return;
                    }

                    for (key in resources) {
                        if (!resources.hasOwnProperty(key)) continue;
                        resource = resources[key]

                        // Nominate the LRU.
                        if (resource.lastUsed) {
                            if (resource.lastUsed <= lruTime) {
                                lruKey = key;
                                lruTime = resource.lastUsed;
                            }
                        // If a resource has not been used, it's the LRU.
                        } else {
                            lruKey = key;
                            lruTime = 0;
                            break;
                        }
                    }

                    delete resources[lruKey];
                    persist();
                    return;
                }

                if (callback) callback();

            }, 0);
        })();
    }

  , reset = function(val) {
        cache = val || {};
    }

  , localStorageKey = 'Mobify-Combo-Cache-v1.0'

    // In memory cache.
  , cache = {}

  , httpCache = Mobify.httpCache = {
        get: get
      , set: set
      , load: load
      , save: save
      , reset: reset
    }

})(this, Mobify);

/**
 * httpCache.utils: HTTP 1.1 Caching header helpers.
 */
(function(httpCache) {
   /**
    * Regular expressions for cache-control directives.
    * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
    */
var ccPublic = /^\s*public\s*$/
  , ccPrivate = /^\s*private\s*$/
  , ccNoCache = /^\s*no-cache\s*$/
  , ccNoStore = /^\s*no-store\s*$/
  , ccNoTransform = /^\s*no-store\s*$/
  , ccMustRevalidate = /^\s*must-revalidate\s*$/
  , ccProxyRevalidate = /^\s*proxy-revalidate\s*$/
  , ccMaxAge = /^\s*max-age\s*=\s*(\d+)\s*$/
  , ccSMaxAge = /^\s*s-maxage\s*=\s*(\d+)\s*$/

    /**
     * Returns an object representing a parsed HTTP 1.1 Cache-Control directive.
     * The object will have keys for all cache-control response properties.
     * Properties are null or boolean except for max-age and s-maxage which
     * should be positive integers
     */
  , ccParse = function (directive) {
        var parsedDirective = {
                'public': null
              , 'private': null
              , 'no-cache': null
              , 'no-store': null
              , 'no-transform': null
              , 'must-revalidate': null
              , 'proxy-revalidate': null
              , 'max-age': null
              , 's-maxage': null
            };

        directive.split(',').forEach(function(d) {
            var matches;
            if (ccPublic.test(d)) parsedDirective['public'] = true;
            else if (ccPrivate.test(d)) parsedDirective['private'] = true;
            else if (ccNoCache.test(d)) parsedDirective['no-cache'] = true;
            else if (ccNoStore.test(d)) parsedDirective['no-store'] = true;
            else if (ccNoTransform.test(d)) parsedDirective['no-transform'] = true;
            else if (ccMustRevalidate.test(d)) {
                parsedDirective['must-revalidate'] = true;
            } else if (ccProxyRevalidate.test(d)) {
                parsedDirective['proxy-revalidate'] = true;
            } else if (matches = ccMaxAge.exec(d)) {
                parsedDirective['max-age'] = matches[1];
            } else if (matches = ccSMaxAge.exec(d)) {
                parsedDirective['s-maxage'] = matches[1];
            }
        })

        return parsedDirective;
    }

    /**
     * Returns the age of `resource` in milliseconds.
     */
  , getAge = function(resource) {
        var date = resource.headers.date;
        if (date) return Date.now() - Date.parse(date);
        return 0;
    }

    /**
     * Returns the freshness lifetime of `resource` in milliseconds.
     */
  , getFreshnessLifetime = function(resource) {
        var cacheControl
          , maxAge
          , expires
          , now = Date.now()
          , headers = resource.headers
          , date;

        // If there's a max-age cache-control directive, return it
        if ((cacheControl = headers['cache-control']) && (date = headers.date)) {

            // Parse out the cache control header and date
            date = Date.parse(headers.date);
            cacheControl = ccParse(cacheControl);

            if ((cacheControl['max-age'] !== null) &&
                (cacheControl['private'] === null) &&
                (cacheControl['no-store'] === null) &&
                (cacheControl['no-cache'] === null)) {

                // The max-age header is represented in seconds, convert to ms.
                maxAge = parseInt(cacheControl['max-age']) * 1000;

                expires = date + maxAge;
                return expires - now;
            }
        }

        // Otherwise, try to compute a max-age from the Expires header.
        if (expires = headers.expires) {
            expires = Date.parse(expires);
            return expires - now;
        }

        // Otherwise, the freshness lifetime is 0.
        return 0;
    }

    /**
     * Returns a max-age value for `resource` in milliseconds.
     */
  , maxAge = function(resource) {
        return getFreshnessLifetime(resource) - getAge(resource);
    }

  , utils = httpCache.utils = {
        /**
         * Returns a data URI for `resource` suitable for executing the script.
         */
        dataURI: function(resource) {
            var contentType = resource.headers['content-type'] || 'application/x-javascript'
            return 'data:' + contentType + (!resource.text
                 ? (';base64,' + resource.body)
                 : (',' + encodeURIComponent(resource.body)));
        }

        /**
         * Returns `true` if `resource` is stale by HTTP 1.1 caching rules.
         */
      , isStale: function(resource) {
            return getAge(resource) > maxAge(resource)
        }
    };

})(Mobify.httpCache);

/**
 * combineScripts: Clientside API to the combo service.
 */
(function(window, document, Mobify) {

var httpCache = Mobify.httpCache

  , absolutify = document.createElement('a')

  , combineScripts = function($els) {
        var $scripts = $els.filter(defaults.selector).add($els.find(defaults.selector)).remove()
          , uncached = []
          , combo = false
          , bootstrap
          , url;

        // Fastfail if there are no scripts or if required modules are missing.
        if (!$scripts.length || !window.localStorage || !window.JSON) return $scripts;

        httpCache.load();

        $scripts.filter('[' + defaults.attribute + ']').each(function() {
            combo = true
            absolutify.href = this.getAttribute(defaults.attribute);
            url = absolutify.href;

            if (!httpCache.get(url)) {
                uncached.push(url);
            }

            this.removeAttribute(defaults.attribute);
            this.className += ' x-combo';
            this.innerHTML = defaults.execCallback + "('" + url + "');";
        });

        if (!combo) return $scripts;

        bootstrap = document.createElement('script')

        if (uncached.length) {
            bootstrap.src = getURL(uncached, defaults.loadCallback);
        } else {
            bootstrap.innerHTML = defaults.loadCallback + '()';
        }

        $scripts = $(bootstrap).add($scripts);
        return $scripts;
    }

  , defaults = combineScripts.defaults = {
        selector: 'script'
      , attribute: 'x-src'
      , endpoint: '//combo.mobify.com/jsonp/'
      , execCallback: 'Mobify.combo.exec'
      , loadCallback: 'Mobify.combo.load'
    }

  , combo = Mobify.combo = {
        /**
         * Emit a <script> tag to execute the contents of `url` using
         * `document.write`. Prefer loading contents from cache.
         */
        exec: function(url) {
            var resource;

            if (resource = httpCache.get(url, true)) {
                url = httpCache.utils.dataURI(resource);
            }

            document.write('<script src="' + url + '"><\/script>');
        }

        /**
         * Callback for loading the httpCache and storing the results of a combo
         * query.
         */
      , load: function(resources) {
            var resource, i, save = false;

            httpCache.load()

            if (!resources) return;

            for (i = 0; i < resources.length; i++) {
                resource = resources[i];
                if (resource.status == 'ready') {
                    save = true;
                    httpCache.set(resource.url, resource)
                }
            }

            if (save) httpCache.save();
        }
    }

    /**
     * Returns a URL suitable for use with the combo service.
     */
  , getURL = function(urls, callback) {
        return defaults.endpoint + callback + '/' + JSONURIencode(urls);
    }

  , JSONURIencode = Mobify.JSONURIencode = function(obj) {
        return encodeURIComponent(JSON.stringify(obj));
    };

Mobify.$.fn.combineScripts = function() {
    return combineScripts.call(window, this)
}

Mobify.cssURL = function(obj) {
    return '//combo.mobify.com/css/' + JSONURIencode(obj)
}

})(this, document, Mobify);<commit_msg>Combo: Properly reference $ inside httpCache. `combo.getURL` always returns the same URL for the same set of URLs.<commit_after>/**
 * httpCache: An implementation of an in memory HTTP cache that persists data to
 * localStorage.
 */
(function(window, Mobify) {
    /**
     * Retrieve `key` from the cache. Mark as used if `increment` is set.
     */
var get = function(key, increment) {
        // Ignore anchors.
        var resource = cache[key.split('#')[0]];

        if (resource && increment) {
            resource.lastUsed = Date.now();
            resource.useCount = resource.useCount++ || 1;
        }

        return resource;
    }

  , set = function(key, val) {
        cache[key] = val;
    }

    /**
     * Load the persistent cache into memory. Ignore stale resources.
     */
  , load = function() {
        var data = localStorage.getItem(localStorageKey)
          , key;

        if (data === null) return;

        try {
            data = JSON.parse(data)
        } catch(err) {
            return;
        }

        for (key in data) {
            if (data.hasOwnProperty(key) && !httpCache.utils.isStale(data[key])) {
                set(key, data[key]);
            }
        }
    }

    /**
     * Save the in-memory cache to disk. If the disk is full, use LRU to drop
     * resources until it will fit on disk.
     */
  , save = function(callback) {
        var resources = {}
          , resource
          , attempts = 10
          , key
          , serialized
            // End of time.
          , lruTime = 9007199254740991
          , lruKey;

        for (key in cache) {
            if (cache.hasOwnProperty(key)) {
                resources[key] = cache[key]
            }
        }

        (function persist() {
            setTimeout(function() {
                try {
                    serialized = JSON.stringify(resources);
                } catch(err) {
                    if (callback) callback(err);
                    return;
                }

                try {
                    localStorage.setItem(localStorageKey, serialized)
                } catch(err) {
                    if (!--attempts) {
                        if (callback) callback(err);
                        return;
                    }

                    for (key in resources) {
                        if (!resources.hasOwnProperty(key)) continue;
                        resource = resources[key]

                        // Nominate the LRU.
                        if (resource.lastUsed) {
                            if (resource.lastUsed <= lruTime) {
                                lruKey = key;
                                lruTime = resource.lastUsed;
                            }
                        // If a resource has not been used, it's the LRU.
                        } else {
                            lruKey = key;
                            lruTime = 0;
                            break;
                        }
                    }

                    delete resources[lruKey];
                    persist();
                    return;
                }

                if (callback) callback();

            }, 0);
        })();
    }

  , reset = function(val) {
        cache = val || {};
    }

  , localStorageKey = 'Mobify-Combo-Cache-v1.0'

    // In memory cache.
  , cache = {}

  , httpCache = Mobify.httpCache = {
        get: get
      , set: set
      , load: load
      , save: save
      , reset: reset
    }

})(this, Mobify);

/**
 * httpCache.utils: HTTP 1.1 Caching header helpers.
 */
(function(httpCache) {
   /**
    * Regular expressions for cache-control directives.
    * See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
    */
var ccPublic = /^\s*public\s*$/
  , ccPrivate = /^\s*private\s*$/
  , ccNoCache = /^\s*no-cache\s*$/
  , ccNoStore = /^\s*no-store\s*$/
  , ccNoTransform = /^\s*no-store\s*$/
  , ccMustRevalidate = /^\s*must-revalidate\s*$/
  , ccProxyRevalidate = /^\s*proxy-revalidate\s*$/
  , ccMaxAge = /^\s*max-age\s*=\s*(\d+)\s*$/
  , ccSMaxAge = /^\s*s-maxage\s*=\s*(\d+)\s*$/

    /**
     * Returns an object representing a parsed HTTP 1.1 Cache-Control directive.
     * The object will have keys for all cache-control response properties.
     * Properties are null or boolean except for max-age and s-maxage which
     * should be positive integers
     */
  , ccParse = function (directive) {
        var parsedDirective = {
                'public': null
              , 'private': null
              , 'no-cache': null
              , 'no-store': null
              , 'no-transform': null
              , 'must-revalidate': null
              , 'proxy-revalidate': null
              , 'max-age': null
              , 's-maxage': null
            };

        directive.split(',').forEach(function(d) {
            var matches;
            if (ccPublic.test(d)) parsedDirective['public'] = true;
            else if (ccPrivate.test(d)) parsedDirective['private'] = true;
            else if (ccNoCache.test(d)) parsedDirective['no-cache'] = true;
            else if (ccNoStore.test(d)) parsedDirective['no-store'] = true;
            else if (ccNoTransform.test(d)) parsedDirective['no-transform'] = true;
            else if (ccMustRevalidate.test(d)) {
                parsedDirective['must-revalidate'] = true;
            } else if (ccProxyRevalidate.test(d)) {
                parsedDirective['proxy-revalidate'] = true;
            } else if (matches = ccMaxAge.exec(d)) {
                parsedDirective['max-age'] = matches[1];
            } else if (matches = ccSMaxAge.exec(d)) {
                parsedDirective['s-maxage'] = matches[1];
            }
        })

        return parsedDirective;
    }

    /**
     * Returns the age of `resource` in milliseconds.
     */
  , getAge = function(resource) {
        var date = resource.headers.date;
        if (date) return Date.now() - Date.parse(date);
        return 0;
    }

    /**
     * Returns the freshness lifetime of `resource` in milliseconds.
     */
  , getFreshnessLifetime = function(resource) {
        var cacheControl
          , maxAge
          , expires
          , now = Date.now()
          , headers = resource.headers
          , date;

        // If there's a max-age cache-control directive, return it
        if ((cacheControl = headers['cache-control']) && (date = headers.date)) {

            // Parse out the cache control header and date
            date = Date.parse(headers.date);
            cacheControl = ccParse(cacheControl);

            if ((cacheControl['max-age'] !== null) &&
                (cacheControl['private'] === null) &&
                (cacheControl['no-store'] === null) &&
                (cacheControl['no-cache'] === null)) {

                // The max-age header is represented in seconds, convert to ms.
                maxAge = parseInt(cacheControl['max-age']) * 1000;

                expires = date + maxAge;
                return expires - now;
            }
        }

        // Otherwise, try to compute a max-age from the Expires header.
        if (expires = headers.expires) {
            expires = Date.parse(expires);
            return expires - now;
        }

        // Otherwise, the freshness lifetime is 0.
        return 0;
    }

    /**
     * Returns a max-age value for `resource` in milliseconds.
     */
  , maxAge = function(resource) {
        return getFreshnessLifetime(resource) - getAge(resource);
    }

  , utils = httpCache.utils = {
        /**
         * Returns a data URI for `resource` suitable for executing the script.
         */
        dataURI: function(resource) {
            var contentType = resource.headers['content-type'] || 'application/x-javascript'
            return 'data:' + contentType + (!resource.text
                 ? (';base64,' + resource.body)
                 : (',' + encodeURIComponent(resource.body)));
        }

        /**
         * Returns `true` if `resource` is stale by HTTP 1.1 caching rules.
         */
      , isStale: function(resource) {
            return getAge(resource) > maxAge(resource)
        }
    };

})(Mobify.httpCache);

/**
 * combineScripts: Clientside API to the combo service.
 */
(function(window, document, Mobify) {

var $ = Mobify.$

  , httpCache = Mobify.httpCache

  , absolutify = document.createElement('a')

  , combineScripts = function($els) {
        var $scripts = $els.filter(defaults.selector).add($els.find(defaults.selector)).remove()
          , uncached = []
          , combo = false
          , bootstrap
          , url;

        // Fastfail if there are no scripts or if required modules are missing.
        if (!$scripts.length || !window.localStorage || !window.JSON) return $scripts;

        httpCache.load();

        $scripts.filter('[' + defaults.attribute + ']').each(function() {
            combo = true
            absolutify.href = this.getAttribute(defaults.attribute);
            url = absolutify.href;

            if (!httpCache.get(url)) {
                uncached.push(url);
            }

            this.removeAttribute(defaults.attribute);
            this.className += ' x-combo';
            this.innerHTML = defaults.execCallback + "('" + url + "');";
        });

        if (!combo) return $scripts;

        bootstrap = document.createElement('script')

        if (uncached.length) {
            bootstrap.src = getURL(uncached, defaults.loadCallback);
        }

        $scripts = $(bootstrap).add($scripts);
        return $scripts;
    }

  , defaults = combineScripts.defaults = {
        selector: 'script'
      , attribute: 'x-src'
      , endpoint: '//combo.mobify.com/jsonp/'
      , execCallback: 'Mobify.combo.exec'
      , loadCallback: 'Mobify.combo.load'
    }

  , combo = Mobify.combo = {
        /**
         * Emit a <script> tag to execute the contents of `url` using
         * `document.write`. Prefer loading contents from cache.
         */
        exec: function(url) {
            var resource;

            if (resource = httpCache.get(url, true)) {
                url = httpCache.utils.dataURI(resource);
            }

            document.write('<script src="' + url + '"><\/script>');
        }

        /**
         * Callback for loading the httpCache and storing the results of a combo
         * query.
         */
      , load: function(resources) {
            var resource, i, save = false;

            httpCache.load()

            if (!resources) return;

            for (i = 0; i < resources.length; i++) {
                resource = resources[i];
                if (resource.status == 'ready') {
                    save = true;
                    httpCache.set(resource.url, resource)
                }
            }

            if (save) httpCache.save();
        }
    }

    /**
     * Returns a URL suitable for use with the combo service. Sorted to generate
     * cacheable URLs.
     */
  , getURL = function(urls, callback) {
        return defaults.endpoint + callback + '/' + JSONURIencode(urls.slice().sort());
    }

  , JSONURIencode = Mobify.JSONURIencode = function(obj) {
        return encodeURIComponent(JSON.stringify(obj));
    };

$.fn.combineScripts = function() {
    return combineScripts.call(window, this)
}

Mobify.cssURL = function(obj) {
    return '//combo.mobify.com/css/' + JSONURIencode(obj)
}

})(this, document, Mobify);<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright 2015 France Labs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
AjaxFranceLabs.FacetDuplicates = AjaxFranceLabs.TableWidget.extend({

	//Methods

	update : function() {
		var self = this, data = this.assocTags(this.manager.response.facet_counts.facet_fields[this.field]), max = (data.length > this.maxDisplay) ? this.maxDisplay : data.length, elm = $(this.elm), compteur = 0;
		// compteur is a counter used to iterate all hashes that should be converted in names
		if (data.length!==0){
			$.get(self.manager.serverUrl+'select?q=*%3A*&rows=1&fl=title&wt=json&indent=true&hl=off&_=1433765381298&fq=signature%3A%22'+data[compteur].name+'%22',function(responseJSON){
				data[compteur].name=responseJSON.response.docs[0].title[0]; // we save the response in the same array
				compteur++;
				if (compteur<data.length)
					$.get(self.manager.serverUrl+'select?q=*%3A*&rows=1&fl=title&wt=json&indent=true&hl=off&_=1433765381298&fq=signature%3A%22'+data[compteur].name+'%22',this);
				else{
					elm.find('ul').empty();
					for (var i = 0; i < max; i++) {
						if (data[i].name !== ''){
							elm.find('ul').append('
');
							elm.find('ul li:last').append('');

							elm.find('ul li:last label').append('
').append('
');
							elm.find('ul li:last .filterFacetCheck').append('<input type="checkbox" value="' + data[i].name + '"/>');
							elm.find('ul li:last .filterFacetCheck input').attr('id',data[i].name);
							if (self.manager.store.find('fq', new RegExp(self.field + ':' + AjaxFranceLabs.Parameter.escapeValue(data[i].name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&").replace(/\\/g,"\\\\")) + '[ )]')))
								elm.find('ul li:last .filterFacetCheck input').attr('checked', 'checked').parents('li').addClass('selected');
							elm.find('ul li:last .filterFacetCheck input').change(function() {
								if ($(this).attr('checked') == 'checked') {
									if(self.selectionType === 'ONE' && elm.find('ul li .filterFacetCheck input:checked').not(this).length)
										self.remove(elm.find('ul li .filterFacetCheck input:checked').not(this).val());
									self.clickHandler();
									self.selectHandler($(this).val().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"));
								} else {
									self.clickHandler();
									self.unselectHandler($(this).val().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"));
								}
							});
							elm.find('ul li:last .filterFacetCheck').append('');
							elm.find('ul li:last .filterFacetCheck label').attr('for', data[i].name).append(' '+''+AjaxFranceLabs.tinyString(data[i].name, 19)+'').append(' (' + data[i].nb + ')');
						}
					}
				}
			});
		}

		if (this.pagination) {
			this.pagination.source = $('ul', this.elm);
			this.pagination.updatePages();
		}
		this.sortBy(this.sort);
	},
});<commit_msg>Upgrading the deduplication<commit_after>/*******************************************************************************
 * Copyright 2015 France Labs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
AjaxFranceLabs.FacetDuplicates = AjaxFranceLabs.TableWidget.extend({

	/*
	 * this method is used to build the HTML of the facet
	 */

	update : function() {
		var self = this, data = this.assocTags(this.manager.response.facet_counts.facet_fields[this.field]), max = (data.length > this.maxDisplay) ? this.maxDisplay : data.length, elm = $(this.elm), compteur = 0, names = [];
		// compteur is a counter used to iterate in all hashes that should be converted in names
		if (data.length!==0){
			//show the facet signature if we have a duplicated file
			$("#facet_signature").show();
			$.get(self.manager.serverUrl+'select?q=*%3A*&rows=1&fl=title&wt=json&indent=true&hl=off&_=1433765381298&fq=signature%3A%22'+data[compteur].name+'%22',function recursive(responseJSON){
				names[compteur]=responseJSON.response.docs[0].title[0]; // we save the response in array
				compteur++;
				if (compteur<data.length)
					$.get(self.manager.serverUrl+'select?q=*%3A*&rows=1&fl=title&wt=json&indent=true&hl=off&_=1433765381298&fq=signature%3A%22'+data[compteur].name+'%22',recursive);
				else{
					elm.find('ul').empty();
					for (var i = 0; i < max; i++) {
						if (names[i] !== ''){
							elm.find('ul').append('
');
							elm.find('ul li:last').append('');

							elm.find('ul li:last label').append('
').append('
');
							elm.find('ul li:last .filterFacetCheck').append('<input type="checkbox" value="' + data[i].name + '"/>');
							elm.find('ul li:last .filterFacetCheck input').attr('id',data[i].name);
							if (self.manager.store.find('fq', new RegExp(self.field + ':' + AjaxFranceLabs.Parameter.escapeValue(data[i].name.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&").replace(/\\/g,"\\\\")) + '[ )]')))
								elm.find('ul li:last .filterFacetCheck input').attr('checked', 'checked').parents('li').addClass('selected');
							elm.find('ul li:last .filterFacetCheck input').change(function() {
								if ($(this).attr('checked') == 'checked') {
									if(self.selectionType === 'ONE' && elm.find('ul li .filterFacetCheck input:checked').not(self).length)
										self.remove(elm.find('ul li .filterFacetCheck input:checked').not(this).val());
									self.clickHandler();
									self.selectHandler($(this).val().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"));
								} else {
									self.clickHandler();
									self.unselectHandler($(this).val().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"));
								}
							});
							elm.find('ul li:last .filterFacetCheck').append('');
							elm.find('ul li:last .filterFacetCheck label').attr('for', data[i].name).append(' '+''+AjaxFranceLabs.tinyString(names[i], 19)+'').append(' (' + data[i].nb + ')');
						}
					}
					if (self.pagination) {
						self.pagination.source = $('ul', self.elm);
						self.pagination.updatePages();
					}
					self.sortBy(self.sort);
				}
			});
		}else{
			// if there's no duplicated file, hide the facet
			$("#facet_signature").hide();
		}
	},

});<|endoftext|>


<commit_before>var redis = require("redis");
var config = require("./config").url;

if (process.env.REDISTOGO_URL) {
    // TODO: redistogo connection
    var rtg   = require("url").parse(process.env.REDISTOGO_URL);
    var redis = require("redis").createClient(rtg.port, rtg.hostname);
    var client = redis.createClient(rtg.port, rtg.hostname);
    redis.auth(rtg.auth.split(":")[1]);
} else {
    var client = redis.createClient();
}

// read all tasks from redis and return
function readAllTasks(callback) {
  var taskArray = [];

  // fetches set of tasks ids
  client.smembers("idSet", function(err, reply) {
    reply.forEach(function(key) {

      // for each id return all hash info
      client.hgetall(key, function(err, reply) {
        console.log("reply: ", reply);
        taskArray.push(reply);
      });
    });
  });

  // Remove setTimeout when we have the time
  setTimeout(function() {
    callback(null, taskArray);
  }, 50);
}

function storeTask(task, category, callback) {

  client.select(0, function() {

    // Random id for each task
    var id = Math.floor(Math.random() * 1000);
    client.hmset(id, {
      "id": id,
      "taskName": task,
      "done": "not-done",
      "category": category
    }, callback);
    client.sadd("idSet", id, function(err, reply) {

      if (err) {
        console.log("id sadd err: ", err);
      } else {
        console.log("id sadd successful");

        // once new task is added, return all data
        readAllTasks(callback);
      }
    });
  });
}

function updateStatus(taskId, taskStatus, callback) {

  client.select(0, function() {

    var update;
    if (taskStatus == "not-done") {
      update = "done"
    } else {
        update = "not-done"
      }

    client.hset(taskId, "done", update, function(err, reply) {

      if (err) {
        console.log("status update err:", err);
      } else {
        console.log("status update successful");

        readAllTasks(callback);
      }
    })
  });
}

module.exports = {
  readAllTasks: readAllTasks,
  storeTask: storeTask,
  updateStatus: updateStatus
};
<commit_msg>redis<commit_after>var redis = require("redis");
var config = require("./config").url;

if (process.env.REDISTOGO_URL) {
    // TODO: redistogo connection
    var rtg   = require("url").parse(process.env.REDISTOGO_URL);
    var client = redis.createClient(rtg.port, rtg.hostname);
    redis.auth(rtg.auth.split(":")[1]);
} else {
    var client = redis.createClient();
}

// read all tasks from redis and return
function readAllTasks(callback) {
  var taskArray = [];

  // fetches set of tasks ids
  client.smembers("idSet", function(err, reply) {
    reply.forEach(function(key) {

      // for each id return all hash info
      client.hgetall(key, function(err, reply) {
        console.log("reply: ", reply);
        taskArray.push(reply);
      });
    });
  });

  // Remove setTimeout when we have the time
  setTimeout(function() {
    callback(null, taskArray);
  }, 50);
}

function storeTask(task, category, callback) {

  client.select(0, function() {

    // Random id for each task
    var id = Math.floor(Math.random() * 1000);
    client.hmset(id, {
      "id": id,
      "taskName": task,
      "done": "not-done",
      "category": category
    }, callback);
    client.sadd("idSet", id, function(err, reply) {

      if (err) {
        console.log("id sadd err: ", err);
      } else {
        console.log("id sadd successful");

        // once new task is added, return all data
        readAllTasks(callback);
      }
    });
  });
}

function updateStatus(taskId, taskStatus, callback) {

  client.select(0, function() {

    var update;
    if (taskStatus == "not-done") {
      update = "done"
    } else {
        update = "not-done"
      }

    client.hset(taskId, "done", update, function(err, reply) {

      if (err) {
        console.log("status update err:", err);
      } else {
        console.log("status update successful");

        readAllTasks(callback);
      }
    })
  });
}

module.exports = {
  readAllTasks: readAllTasks,
  storeTask: storeTask,
  updateStatus: updateStatus
};
<|endoftext|>


<commit_before>/* global  __dirname */
/* global  process */

'use strict';

import http from 'http';
import path from 'path';
import express from 'express';
import exphbs from 'express-handlebars';
import PathConfig from './server/models/path-config';
import bodyParser from 'body-parser';
import compression from 'compression';
import session from 'express-session';
import _ from 'lodash/core';
import Service from './api/mock.service';
import HandlebarsHelpers from './helpers/handlebars';
import { requireHttps } from './helpers/routing';

let app = express();

// enable g-zip compression
app.use(compression());

// Setup body parser middleware for parsing POST request bodies
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// require HTTPS
app.use(requireHttps);

let viewsDir = './dist/templates';

// setup express to use handlebars as the templating engine
let hbs = exphbs.create({
    defaultLayout: 'main',
    layoutsDir: path.join(__dirname, `${viewsDir}/layouts`),
    partialsDir: path.join(__dirname, `${viewsDir}/partials`),
    extname: '.hbs'
});
app.set('views', path.join(__dirname, `${viewsDir}`));
app.engine('hbs', hbs.engine);
app.set('view engine', 'hbs');

// register handlebar helpers
HandlebarsHelpers.registerHandlebarsHelpers();

// setup server for static assets
app.use('/', express.static(`${process.env.outputFolder}`, { maxAge: 604800000 }));

// setup session middleware
app.use(session({
    secret: 'test',
    cookie: { maxAge: 1200000 },
    unset: 'destroy',
    resave: true,
    saveUninitialized: false,
    sameSite: true
}));

let service = new Service();

let pathConfig = new PathConfig(`/../../${process.env.outputFolder}`);

// setup server urls
app.post('/learn', (req, res) => {
    if (req.body) {
        // store the user's selection in the session
        req.session.answer = req.body.answer;
        // notify the request that it was successful
        req.session.isValid = req.body.answer === 'name; first name';
    }

    res.redirect('/learn' + '?level=' + (req.body.level || 1));
});

// setup server urls
app.get('/*', (req, res) => {
    // extract the path from the url
    let urlSections = req.path.split('/');
    urlSections = urlSections.filter((sectionString) => {
        return sectionString.length > 0;
    });

    let urlPath = null;
    if (urlSections.length === 0) {
        if (urlSections[0] === '') {}
        urlPath = '/';
    } else {
        urlPath = '/' + urlSections.join('/');
    }

    // retrieve the path data
    pathConfig.getConfig(urlPath).then((pathConfigData) => {
        if (!pathConfigData) {
            res.status(404).send();
            return;
        }

        // retrieve API data if we are on the '/learn' page
        if (urlPath === '/learn') {
            service.getData(req.query.level).then((apiData) => {
                // no data retrieved because user has completed the course
                if (!apiData) {
                    pathConfigData.data.view = 'finish';
                } else {
                    pathConfigData.data.view = 'learn';
                }

                pathConfigData.data.apiData = apiData;

                // read data from the session
                if (req.session.answer) {
                    // update the user's selected answer
                    let answer = _.find(pathConfigData.data.apiData.answers, (item) => {
                        return item.value === req.session.answer;
                    });

                    answer.isSelected = true;
                    if (answer.isCorrect) {
                        // allow access to the next level
                        pathConfigData.allowNextLevel = true;
                    }

                    // clear the session
                    delete req.session.answer;
                    delete req.session.isValid;
                }

                // render the response
                res.render(pathConfigData.data.view, pathConfigData);
            });
        } else {
            // render the response
            res.render(pathConfigData.data.view, pathConfigData);
        }
    });
});

let port = process.env.port || 3000;
http.createServer(app).listen(port, () => {
    return console.log(`Running Example on localhost:${port}`);
});

module.exports = app;
<commit_msg>Added .well-known folder for LetsEncrypt<commit_after>/* global  __dirname */
/* global  process */

'use strict';

import http from 'http';
import path from 'path';
import express from 'express';
import exphbs from 'express-handlebars';
import PathConfig from './server/models/path-config';
import bodyParser from 'body-parser';
import compression from 'compression';
import session from 'express-session';
import _ from 'lodash/core';
import Service from './api/mock.service';
import HandlebarsHelpers from './helpers/handlebars';
import { requireHttps } from './helpers/routing';

let app = express();

// enable g-zip compression
app.use(compression());

// Setup body parser middleware for parsing POST request bodies
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// require HTTPS
app.use(requireHttps);

let viewsDir = './dist/templates';

// setup express to use handlebars as the templating engine
let hbs = exphbs.create({
    defaultLayout: 'main',
    layoutsDir: path.join(__dirname, `${viewsDir}/layouts`),
    partialsDir: path.join(__dirname, `${viewsDir}/partials`),
    extname: '.hbs'
});
app.set('views', path.join(__dirname, `${viewsDir}`));
app.engine('hbs', hbs.engine);
app.set('view engine', 'hbs');

// register handlebar helpers
HandlebarsHelpers.registerHandlebarsHelpers();

// setup server for static assets
app.use('/', express.static(`${process.env.outputFolder}`, { maxAge: 604800000 }));

// https://medium.com/@yash.kulshrestha/using-lets-encrypt-with-express-e069c7abe625
app.use('/.well-known', express.static(path.join(__dirname, '.well-known')));

// setup session middleware
app.use(session({
    secret: 'test',
    cookie: { maxAge: 1200000 },
    unset: 'destroy',
    resave: true,
    saveUninitialized: false,
    sameSite: true
}));

let service = new Service();

let pathConfig = new PathConfig(`/../../${process.env.outputFolder}`);

// setup server urls
app.post('/learn', (req, res) => {
    if (req.body) {
        // store the user's selection in the session
        req.session.answer = req.body.answer;
        // notify the request that it was successful
        req.session.isValid = req.body.answer === 'name; first name';
    }

    res.redirect('/learn' + '?level=' + (req.body.level || 1));
});

// setup server urls
app.get('/*', (req, res) => {
    // extract the path from the url
    let urlSections = req.path.split('/');
    urlSections = urlSections.filter((sectionString) => {
        return sectionString.length > 0;
    });

    let urlPath = null;
    if (urlSections.length === 0) {
        if (urlSections[0] === '') {}
        urlPath = '/';
    } else {
        urlPath = '/' + urlSections.join('/');
    }

    // retrieve the path data
    pathConfig.getConfig(urlPath).then((pathConfigData) => {
        if (!pathConfigData) {
            res.status(404).send();
            return;
        }

        // retrieve API data if we are on the '/learn' page
        if (urlPath === '/learn') {
            service.getData(req.query.level).then((apiData) => {
                // no data retrieved because user has completed the course
                if (!apiData) {
                    pathConfigData.data.view = 'finish';
                } else {
                    pathConfigData.data.view = 'learn';
                }

                pathConfigData.data.apiData = apiData;

                // read data from the session
                if (req.session.answer) {
                    // update the user's selected answer
                    let answer = _.find(pathConfigData.data.apiData.answers, (item) => {
                        return item.value === req.session.answer;
                    });

                    answer.isSelected = true;
                    if (answer.isCorrect) {
                        // allow access to the next level
                        pathConfigData.allowNextLevel = true;
                    }

                    // clear the session
                    delete req.session.answer;
                    delete req.session.isValid;
                }

                // render the response
                res.render(pathConfigData.data.view, pathConfigData);
            });
        } else {
            // render the response
            res.render(pathConfigData.data.view, pathConfigData);
        }
    });
});

let port = process.env.port || 3000;
http.createServer(app).listen(port, () => {
    return console.log(`Running Example on localhost:${port}`);
});

module.exports = app;
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import {Field, formValueSelector} from 'redux-form'

import { Input, Switch } from '../../../../components/Form/Form'

import { Row, Col, Alert, Button, Icon } from 'antd'

const contactTypes = [
  {
    title: 'Primary Author',
    field: 'primary'
  }, {
    title: 'Student Lead',
    field: 'student'
  }, {
    title: 'Organizational Head',
    field: 'organization'
  }, {
    title: 'Budget Director',
    field: 'budget'
  }
]

// import styles from './Introduction.css'
const selector = formValueSelector('create') // <-- same as form name

//  https://stackoverflow.com/questions/43999623/how-to-provide-validations-for-react-js-form/44005986#44005986
@connect(
  state => {
    // const primary = selector(state, 'contacts.primary.netID')
    const contacts = selector(state, 'contacts')
    const user = state.user
    //  Object destructuring, e.g primary: primary
    return { contacts, user }
    // const primary = selector(state, 'contacts.primary.netID')
    // const budget = selector(state, 'contacts.budget.netID')
    // const organization = selector(state, 'contacts.organization.netID')
    // const student = selector(state, 'contacts.student.netID')
    // const user = state.user
    // //  Object destructuring, e.g primary: primary
    // return { primary, budget, organization, student, user }
  }
)
class Signatures extends React.Component {
  render ({user, contacts} = this.props) {
    return (

        <Row gutter={32} type='flex' justify='space-between' align='bottom'>
          <Col className='gutter-row' xs={24} sm={12}>

Final Signatures

              To submit your proposal to the Committee, all contacts must sign this proposal signature page.

              By signing this proposal, a department pledges general, physical, and financial support to this proposal. Submitting a proposal also represents the department and contact's agreement with STF policies and procedures.

          </Col>
          <Col className='gutter-row' xs={24} sm={12}>
            <Alert message='Last Step:'
              description='Send this link to the contacts listed in your proposal introduction. Ensure their NetID are spelled correctly. Each contact, with the exception of the optional student lead, will need to sign the proposal.'
              type='info' showIcon
            />
            {contactTypes.map((c, i) => (
              <Col key={i} className='gutter-row' xs={24}>
                {contacts[c.field] &&
                <Field name={`signatures.${c.field}`} component={Switch} size='large'
                  checkedChildren={c.title} unCheckedChildren={c.title}
                  disabled={contacts[c.field].netID !== user.netID}
                />
              }
              </Col>
            ))}
          </Col>
        </Row>

    )
  }
}
export default Signatures
<commit_msg>Got signatures to work really well!<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import {Field, formValueSelector} from 'redux-form'

import { Input, Switch } from '../../../../components/Form/Form'

import { Row, Col, Alert, Button, Icon } from 'antd'

const contactTypes = [
  {
    title: 'Primary Author',
    field: 'primary'
  }, {
    title: 'Student Lead',
    field: 'student'
  }, {
    title: 'Organizational Head',
    field: 'organization'
  }, {
    title: 'Budget Director',
    field: 'budget'
  }
]

// import styles from './Introduction.css'
const selector = formValueSelector('create') // <-- same as form name

//  https://stackoverflow.com/questions/43999623/how-to-provide-validations-for-react-js-form/44005986#44005986
@connect(
  state => {
    // const primary = selector(state, 'contacts.primary.netID')
    const contacts = selector(state, 'contacts')
    const user = state.user
    //  Object destructuring, e.g primary: primary
    return { contacts, user }
    // const primary = selector(state, 'contacts.primary.netID')
    // const budget = selector(state, 'contacts.budget.netID')
    // const organization = selector(state, 'contacts.organization.netID')
    // const student = selector(state, 'contacts.student.netID')
    // const user = state.user
    // //  Object destructuring, e.g primary: primary
    // return { primary, budget, organization, student, user }
  }
)
class Signatures extends React.Component {
  render ({user, contacts} = this.props) {
    return (

        <Row gutter={32} type='flex' justify='space-between' align='bottom'>
          <Col className='gutter-row' xs={24} sm={12}>

Final Signatures

              To submit your proposal to the Committee, all contacts must sign this proposal signature page.

              By signing this proposal, a department pledges general, physical, and financial support to this proposal. Submitting a proposal also represents the department and contact's agreement with STF policies and procedures.

          </Col>
          <Col className='gutter-row' xs={24} sm={12}>
            <Alert message='Last Step:'
              description='Send this link to the contacts listed in your proposal introduction. Ensure their NetID are spelled correctly. Each contact, with the exception of the optional student lead, will need to sign the proposal.'
              type='info' showIcon
            />
            {contactTypes.map((c, i) => (
              <Col key={i} className='gutter-row' xs={24}>
                {contacts[c.field] &&

{c.title}: <Field name={`signatures.${c.field}`}
                    component={Switch} size='large'
                      checkedChildren={contacts[c.field].name} unCheckedChildren={contacts[c.field].name}
                      disabled={contacts[c.field].netID !== user.netID}
                    />

              }
              </Col>
            ))}
          </Col>
        </Row>

    )
  }
}
export default Signatures
<|endoftext|>


<commit_before>var todos = Alloy.createCollection('Todo');<commit_msg>added global object support<commit_after>var G = require("globals");
G.globalGet("todos");

G.globalSet("todos",Alloy.createCollection('Todo'));

Ti.API.info(' TODOS ' + JSON.stringify(G.globalGet("todos").toJSON(),null,2));<|endoftext|>