<commit_before>'use strict';

window.define(['react', 'color', 'color-picker-values', 'color-palette', 'slider'], function (React, color, ColorPickerValues, ColorPalette, Slider) {

  var ColorPicker = React.createClass({
    getHSL: function (h, s, l) {
      var hsl = this.state.hsl;
      if (typeof h !== 'undefined') {
        hsl.h = h;
      }
      if (typeof s !== 'undefined') {
        hsl.s = Math.round(s * 100) / 100;
      }
      if (typeof l !== 'undefined') {
        hsl.l = Math.round((1 - l) * 100) / 100;
      }

      return hsl;
    },

    setPoint: function (point) {
      var hsl = this.getHSL(undefined, point.x, point.y);
      var rgb = color.HSLToRGB(hsl.h, hsl.s, hsl.l);
      var hex = color.RGBToHex(rgb.r, rgb.g, rgb.b);

      this.setState({
        point: point,
        hsl: hsl,
        rgb: rgb,
        hex: hex
      });
    },

    setHue: function (h) {
      var hsl = this.getHSL(h);
      var rgb = color.HSLToRGB(hsl.h, hsl.s, hsl.l);
      var hex = color.RGBToHex(rgb.r, rgb.g, rgb.b);

      this.setState({
        hsl: hsl,
        rgb: rgb,
        hex: hex
      });
    },

    togglePicker: function () {
      this.setState({
        active: !this.state.active
      });
    },

    HSLInput: function (value) {
      var content = /hsla?\((.+?)\)/i.exec(value)[1];
      var hslValues = content.split(',');
      if (hslValues.length < 3) {
        return false;
      }
      if (hslValues[1].indexOf('%') < 0 || hslValues[2].indexOf('%') < 0) {
        return false;
      }
      var hsl = {
        h: parseInt(hslValues[0]) / 360,
        s: parseInt(hslValues[1]) / 100,
        l: parseInt(hslValues[2]) / 100
      };

      if (isNaN(hsl.h) || isNaN(hsl.s) || isNaN(hsl.l) ||
        hsl.h < 0 || hsl.h > 1 ||
        hsl.s < 0 || hsl.s > 1 ||
        hsl.l < 0 || hsl.l > 1) {
        return false;
      }

      var rgb = color.HSLToRGB(hsl.h, hsl.s, hsl.l);

      return {
        hsl: hsl,
        rgb: rgb,
        hex: color.RGBToHex(rgb.r, rgb.g, rgb.b)
      };
    },

    RGBInput: function (value) {
      var content = /rgba?\((.+?)\)/i.exec(value)[1];
      var rgbValues = content.split(',');
      if (rgbValues.length < 3) {
        return false;
      }
      var rgb = {
        r: parseInt(rgbValues[0]),
        g: parseInt(rgbValues[1]),
        b: parseInt(rgbValues[2])
      };

      if (isNaN(rgb.r) || isNaN(rgb.g) || isNaN(rgb.b) ||
        rgb.r < 0 || rgb.r > 255 ||
        rgb.g < 0 || rgb.g > 255 ||
        rgb.b < 0 || rgb.b > 255) {
        return false;
      }

      var hsl = color.RGBToHSL(rgb.r, rgb.g, rgb.b);

      return {
        hsl: {
          h: hsl.h / 360,
          s: hsl.s / 100,
          l: hsl.l / 100
        },
        rgb: rgb,
        hex: color.RGBToHex(rgb.r, rgb.g, rgb.b)
      };
    },

    HexInput: function (value) {
      var content = value.replace(/^#/i, '').replace(/\s+?$/i, '');
      if (content.length !== 3 && content.length !== 6) {
        return false;
      }
      var rgb = color.HexToRGB(content);
      var hsl = color.RGBToHSL(rgb.r, rgb.g, rgb.b);

      return {
        hsl: {
          h: hsl.h / 360,
          s: hsl.s / 100,
          l: hsl.l / 100
        },
        rgb: rgb,
        hex: '#'.concat(content)
      };
    },

    getColors: function (value) {
      if (!value) {
        return false;
      }

      if (typeof value !== 'string') {
        value = value.toString();
      }

      // Remove leading white space
      value = value.replace(/^\s+/i, '');

      // HSL matches hsl(anything) or hsla(anything)
      if (value.match(/^hsla?\(.+?\)/i)) {
        return this.HSLInput(value);
      }

      // RGB matches rgb(anything) or rgba(anything)
      if (value.match(/^rgba?\(.+?\)/i)) {
        return this.RGBInput(value);
      }

      // Hex matches #af1 or #acb123
      if (value.match(/^#([a-f0-9][a-f0-9][a-f0-9])([a-f0-9][a-f0-9][a-f0-9])?\s*$/i)) {
        return this.HexInput(value);
      }

      return false;
    },

    componentWillReceiveProps: function (nextProps) {
      if (this.props.value !== nextProps.value) {
        var value = nextProps.value || nextProps.defaultValue;
        var colors = this.getColors(value);

        if (colors) {
          this.setState({
            point: {
              x: colors.hsl.s,
              y: 1 - colors.hsl.l
            },
            hsl: colors.hsl,
            rgb: colors.rgb,
            hex: colors.hex
          });
        }
      }
    },

    getInitialState: function () {
      var value = this.props.value || this.props.defaultValue;
      var colors = this.getColors(value);

      return {
        active: false,
        point: {
          x: colors ? colors.hsl.s : 0,
          y: colors ? 1 - colors.hsl.l : 0
        },
        hsl: {
          h: colors ? colors.hsl.h : 0,
          s: colors ? colors.hsl.s : 0,
          l: colors ? colors.hsl.l : 1
        },
        rgb: {
          r: colors ? colors.rgb.r : 255,
          g: colors ? colors.rgb.g : 255,
          b: colors ? colors.rgb.b : 255
        },
        hex: colors ? colors.hex : '#ffffff'
      };
    },

    render: function () {
      var self = this;
      var picker;

      if (this.state.active) {
        picker = React.createElement(
          'div',
          {
            className: 'picker'
          },
          React.createElement(
            'div',
            {
              className: 'palette'
            },
            React.createElement(
              ColorPalette,
              {
                point: self.state.point,
                onChange: self.setPoint,
                hsl: self.state.hsl,
                rgb: self.state.rgb,
                hex: self.state.hex
              }
            ),
            React.createElement(
              Slider,
              {
                className: 'hue-slider',
                value: this.state.hsl.h,
                orientation: 'vertical',
                onChange: this.setHue
              }
            )
          ),
          React.createElement(
            ColorPickerValues,
            {
              hsl: self.state.hsl,
              rgb: self.state.rgb,
              hex: self.state.hex
            }
          )
        );
      }

      return React.createElement(
        'div',
        {
          className: 'color-picker'
        },
        React.createElement(
          'div',
          {
            className: 'swatch',
            style: {
              backgroundColor: self.props.value || self.props.defaultValue
            },
            onClick: self.togglePicker
          }
        ),
        picker
      );
    }
  });

  return ColorPicker;

});
<commit_msg>Use this instead of self in color picker<commit_after>'use strict';

window.define(['react', 'color', 'color-picker-values', 'color-palette', 'slider'], function (React, color, ColorPickerValues, ColorPalette, Slider) {

  var ColorPicker = React.createClass({
    getHSL: function (h, s, l) {
      var hsl = this.state.hsl;
      if (typeof h !== 'undefined') {
        hsl.h = h;
      }
      if (typeof s !== 'undefined') {
        hsl.s = Math.round(s * 100) / 100;
      }
      if (typeof l !== 'undefined') {
        hsl.l = Math.round((1 - l) * 100) / 100;
      }

      return hsl;
    },

    setPoint: function (point) {
      var hsl = this.getHSL(undefined, point.x, point.y);
      var rgb = color.HSLToRGB(hsl.h, hsl.s, hsl.l);
      var hex = color.RGBToHex(rgb.r, rgb.g, rgb.b);

      this.setState({
        point: point,
        hsl: hsl,
        rgb: rgb,
        hex: hex
      });
    },

    setHue: function (h) {
      var hsl = this.getHSL(h);
      var rgb = color.HSLToRGB(hsl.h, hsl.s, hsl.l);
      var hex = color.RGBToHex(rgb.r, rgb.g, rgb.b);

      this.setState({
        hsl: hsl,
        rgb: rgb,
        hex: hex
      });
    },

    togglePicker: function () {
      this.setState({
        active: !this.state.active
      });
    },

    HSLInput: function (value) {
      var content = /hsla?\((.+?)\)/i.exec(value)[1];
      var hslValues = content.split(',');
      if (hslValues.length < 3) {
        return false;
      }
      if (hslValues[1].indexOf('%') < 0 || hslValues[2].indexOf('%') < 0) {
        return false;
      }
      var hsl = {
        h: parseInt(hslValues[0]) / 360,
        s: parseInt(hslValues[1]) / 100,
        l: parseInt(hslValues[2]) / 100
      };

      if (isNaN(hsl.h) || isNaN(hsl.s) || isNaN(hsl.l) ||
        hsl.h < 0 || hsl.h > 1 ||
        hsl.s < 0 || hsl.s > 1 ||
        hsl.l < 0 || hsl.l > 1) {
        return false;
      }

      var rgb = color.HSLToRGB(hsl.h, hsl.s, hsl.l);

      return {
        hsl: hsl,
        rgb: rgb,
        hex: color.RGBToHex(rgb.r, rgb.g, rgb.b)
      };
    },

    RGBInput: function (value) {
      var content = /rgba?\((.+?)\)/i.exec(value)[1];
      var rgbValues = content.split(',');
      if (rgbValues.length < 3) {
        return false;
      }
      var rgb = {
        r: parseInt(rgbValues[0]),
        g: parseInt(rgbValues[1]),
        b: parseInt(rgbValues[2])
      };

      if (isNaN(rgb.r) || isNaN(rgb.g) || isNaN(rgb.b) ||
        rgb.r < 0 || rgb.r > 255 ||
        rgb.g < 0 || rgb.g > 255 ||
        rgb.b < 0 || rgb.b > 255) {
        return false;
      }

      var hsl = color.RGBToHSL(rgb.r, rgb.g, rgb.b);

      return {
        hsl: {
          h: hsl.h / 360,
          s: hsl.s / 100,
          l: hsl.l / 100
        },
        rgb: rgb,
        hex: color.RGBToHex(rgb.r, rgb.g, rgb.b)
      };
    },

    HexInput: function (value) {
      var content = value.replace(/^#/i, '').replace(/\s+?$/i, '');
      if (content.length !== 3 && content.length !== 6) {
        return false;
      }
      var rgb = color.HexToRGB(content);
      var hsl = color.RGBToHSL(rgb.r, rgb.g, rgb.b);

      return {
        hsl: {
          h: hsl.h / 360,
          s: hsl.s / 100,
          l: hsl.l / 100
        },
        rgb: rgb,
        hex: '#'.concat(content)
      };
    },

    getColors: function (value) {
      if (!value) {
        return false;
      }

      if (typeof value !== 'string') {
        value = value.toString();
      }

      // Remove leading white space
      value = value.replace(/^\s+/i, '');

      // HSL matches hsl(anything) or hsla(anything)
      if (value.match(/^hsla?\(.+?\)/i)) {
        return this.HSLInput(value);
      }

      // RGB matches rgb(anything) or rgba(anything)
      if (value.match(/^rgba?\(.+?\)/i)) {
        return this.RGBInput(value);
      }

      // Hex matches #af1 or #acb123
      if (value.match(/^#([a-f0-9][a-f0-9][a-f0-9])([a-f0-9][a-f0-9][a-f0-9])?\s*$/i)) {
        return this.HexInput(value);
      }

      return false;
    },

    componentWillReceiveProps: function (nextProps) {
      if (this.props.value !== nextProps.value) {
        var value = nextProps.value || nextProps.defaultValue;
        var colors = this.getColors(value);

        if (colors) {
          this.setState({
            point: {
              x: colors.hsl.s,
              y: 1 - colors.hsl.l
            },
            hsl: colors.hsl,
            rgb: colors.rgb,
            hex: colors.hex
          });
        }
      }
    },

    getInitialState: function () {
      var value = this.props.value || this.props.defaultValue;
      var colors = this.getColors(value);

      return {
        active: false,
        point: {
          x: colors ? colors.hsl.s : 0,
          y: colors ? 1 - colors.hsl.l : 0
        },
        hsl: {
          h: colors ? colors.hsl.h : 0,
          s: colors ? colors.hsl.s : 0,
          l: colors ? colors.hsl.l : 1
        },
        rgb: {
          r: colors ? colors.rgb.r : 255,
          g: colors ? colors.rgb.g : 255,
          b: colors ? colors.rgb.b : 255
        },
        hex: colors ? colors.hex : '#ffffff'
      };
    },

    render: function () {
      var picker;

      if (this.state.active) {
        picker = React.createElement(
          'div',
          {
            className: 'picker'
          },
          React.createElement(
            'div',
            {
              className: 'palette'
            },
            React.createElement(
              ColorPalette,
              {
                point: this.state.point,
                onChange: this.setPoint,
                hsl: this.state.hsl,
                rgb: this.state.rgb,
                hex: this.state.hex
              }
            ),
            React.createElement(
              Slider,
              {
                className: 'hue-slider',
                value: this.state.hsl.h,
                orientation: 'vertical',
                onChange: this.setHue
              }
            )
          ),
          React.createElement(
            ColorPickerValues,
            {
              hsl: this.state.hsl,
              rgb: this.state.rgb,
              hex: this.state.hex,
            }
          )
        );
      }

      return React.createElement(
        'div',
        {
          className: 'color-picker'
        },
        React.createElement(
          'div',
          {
            className: 'swatch',
            style: {
              backgroundColor: this.props.value || this.props.defaultValue
            },
            onClick: this.togglePicker
          }
        ),
        picker
      );
    }
  });

  return ColorPicker;

});
<|endoftext|>


<commit_before>!(function () {
    BI.TimeTrigger = BI.inherit(BI.Trigger, {

        _const: {
            COMPARE_FORMAT: "%H:%M:%S",
            COMPLETE_COMPARE_FORMAT: "%Y-%M-%d %H:%M:%S",
            FORMAT_ARRAY: [
                "%H:%M:%S",  // HH:mm:ss
                "%I:%M:%S",  // hh:mm:ss
                "%l:%M:%S",  // h:mm:ss
                "%k:%M:%S",  // H:mm:ss
                "%l:%M:%S %p",  // h:mm:ss a
                "%l:%M:%S %P",  // h:mm:ss a
                "%l:%M",  // h:mm
                "%k:%M",  // H:mm
                "%I:%M",  // hh:mm
                "%H:%M",  // HH:mm
                "%M:%S"   // mm:ss
            ],
            DEFAULT_DATE_STRING: "2000-01-01"
        },

        props: {
            extraCls: "bi-time-trigger",
            value: {},
            format: "",
            allowEdit: false
        },

        render: function () {
            var self = this, o = this.options;
            this.storeTriggerValue = "";
            this.storeValue = o.value;
            return {
                type: "bi.absolute",
                items: [{
                    el: {
                        type: "bi.sign_editor",
                        height: o.height,
                        validationChecker: function (v) {
                            return self._dateCheck(v);
                        },
                        quitChecker: function () {
                            return false;
                        },
                        ref: function (_ref) {
                            self.editor = _ref;
                        },
                        value: this._formatValue(o.value),
                        hgap: 4,
                        allowBlank: true,
                        watermark: BI.isKey(o.watermark) ? o.watermark : BI.i18nText("BI-Basic_Unrestricted"),
                        title: BI.bind(this._getTitle, this),
                        listeners: [{
                            eventName: "EVENT_KEY_DOWN",
                            action: function () {
                                self.fireEvent("EVENT_KEY_DOWN");
                            }
                        }, {
                            eventName: "EVENT_FOCUS",
                            action: function () {
                                self.storeTriggerValue = self.getKey();
                                self.fireEvent("EVENT_FOCUS");
                            }
                        }, {
                            eventName: "EVENT_STOP",
                            action: function () {
                                self.fireEvent("EVENT_STOP");
                            }
                        }, {
                            eventName: "EVENT_VALID",
                            action: function () {
                                self.fireEvent("EVENT_VALID");
                            }
                        }, {
                            eventName: "EVENT_ERROR",
                            action: function () {
                                self.fireEvent("EVENT_ERROR");
                            }
                        }, {
                            eventName: "EVENT_CONFIRM",
                            action: function () {
                                var value = self.editor.getValue();
                                if (BI.isNotNull(value)) {
                                    self.editor.setState(value);
                                }
                                if (BI.isNotEmptyString(value) && !BI.isEqual(self.storeTriggerValue, self.getKey())) {
                                    var date = value.match(/\d+/g);
                                    self.storeValue = {
                                        hour: date[0] | 0,
                                        minute: date[1] | 0,
                                        second: date[2] | 0
                                    };
                                }
                                self.fireEvent("EVENT_CONFIRM");
                            }
                        }, {
                            eventName: "EVENT_START",
                            action: function () {
                                self.fireEvent("EVENT_START");
                            }
                        }, {
                            eventName: "EVENT_CHANGE",
                            action: function () {
                                self.fireEvent("EVENT_CHANGE");
                            }
                        }]
                    },
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0
                }, {
                    el: {
                        type: "bi.text",
                        invisible: o.allowEdit,
                        cls: "show-text",
                        title: BI.bind(this._getTitle, this),
                        hgap: 4
                    },
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0
                }]
            };
        },

        _dateCheck: function (date) {
            var c = this._const;
            return BI.any(c.FORMAT_ARRAY, function (idx, format) {
                return BI.print(BI.parseDateTime(c.DEFAULT_DATE_STRING + " " + date, c.COMPLETE_COMPARE_FORMAT), format) === date;
            });
        },

        _getTitle: function () {
            var storeValue = this.storeValue || {};
            var date = BI.getDate();
            return BI.print(BI.getDate(date.getFullYear(), 0, 1, storeValue.hour, storeValue.minute, storeValue.second), this._getFormatString());
        },

        _getFormatString: function () {
            return this.options.format || this._const.COMPARE_FORMAT;
        },

        _formatValue: function (v) {
            var now = BI.getDate();
            return BI.isNotEmptyObject(v) ? BI.print(BI.getDate(now.getFullYear(), now.getMonth(), now.getDay(), v.hour, v.minute, v.second), this._getFormatString()) : BI.i18nText("BI-Basic_Unrestricted");
        },

        getKey: function () {
            return this.editor.getValue();
        },

        setValue: function (v) {
            this.storeValue = v;
            this.editor.setValue(this._formatValue(v));
        },

        getValue: function () {
            return this.storeValue;
        }

    });
    BI.shortcut("bi.time_trigger", BI.TimeTrigger);
})();<commit_msg>REPORT-18382 fix: 时分秒自定义格式无效<commit_after>!(function () {
    BI.TimeTrigger = BI.inherit(BI.Trigger, {

        _const: {
            COMPARE_FORMAT: "%H:%M:%S",
            COMPLETE_COMPARE_FORMAT: "%Y-%M-%d %H:%M:%S %P",
            FORMAT_ARRAY: [
                "%H:%M:%S",  // HH:mm:ss
                "%I:%M:%S",  // hh:mm:ss
                "%l:%M:%S",  // h:mm:ss
                "%k:%M:%S",  // H:mm:ss
                "%l:%M:%S %p",  // h:mm:ss a
                "%l:%M:%S %P",  // h:mm:ss a
                "%H:%M:%S %p",  // HH:mm:ss a
                "%l:%M",  // h:mm
                "%k:%M",  // H:mm
                "%I:%M",  // hh:mm
                "%H:%M",  // HH:mm
                "%M:%S"   // mm:ss
            ],
            DEFAULT_DATE_STRING: "2000-01-01"
        },

        props: {
            extraCls: "bi-time-trigger",
            value: {},
            format: "",
            allowEdit: false
        },

        render: function () {
            var self = this, o = this.options;
            this.storeTriggerValue = "";
            this.storeValue = o.value;
            return {
                type: "bi.absolute",
                items: [{
                    el: {
                        type: "bi.sign_editor",
                        height: o.height,
                        validationChecker: function (v) {
                            return self._dateCheck(v);
                        },
                        quitChecker: function () {
                            return false;
                        },
                        ref: function (_ref) {
                            self.editor = _ref;
                        },
                        value: this._formatValue(o.value),
                        hgap: 4,
                        allowBlank: true,
                        watermark: BI.isKey(o.watermark) ? o.watermark : BI.i18nText("BI-Basic_Unrestricted"),
                        title: BI.bind(this._getTitle, this),
                        listeners: [{
                            eventName: "EVENT_KEY_DOWN",
                            action: function () {
                                self.fireEvent("EVENT_KEY_DOWN");
                            }
                        }, {
                            eventName: "EVENT_FOCUS",
                            action: function () {
                                self.storeTriggerValue = self.getKey();
                                self.fireEvent("EVENT_FOCUS");
                            }
                        }, {
                            eventName: "EVENT_STOP",
                            action: function () {
                                self.fireEvent("EVENT_STOP");
                            }
                        }, {
                            eventName: "EVENT_VALID",
                            action: function () {
                                self.fireEvent("EVENT_VALID");
                            }
                        }, {
                            eventName: "EVENT_ERROR",
                            action: function () {
                                self.fireEvent("EVENT_ERROR");
                            }
                        }, {
                            eventName: "EVENT_CONFIRM",
                            action: function () {
                                var value = self.editor.getValue();
                                if (BI.isNotNull(value)) {
                                    self.editor.setState(value);
                                }
                                if (BI.isNotEmptyString(value) && !BI.isEqual(self.storeTriggerValue, self.getKey())) {
                                    var date = value.match(/\d+/g);
                                    self.storeValue = {
                                        hour: date[0] | 0,
                                        minute: date[1] | 0,
                                        second: date[2] | 0
                                    };
                                }
                                self.fireEvent("EVENT_CONFIRM");
                            }
                        }, {
                            eventName: "EVENT_START",
                            action: function () {
                                self.fireEvent("EVENT_START");
                            }
                        }, {
                            eventName: "EVENT_CHANGE",
                            action: function () {
                                self.fireEvent("EVENT_CHANGE");
                            }
                        }]
                    },
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0
                }, {
                    el: {
                        type: "bi.text",
                        invisible: o.allowEdit,
                        cls: "show-text",
                        title: BI.bind(this._getTitle, this),
                        hgap: 4
                    },
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0
                }]
            };
        },

        _dateCheck: function (date) {
            var c = this._const;
            return BI.any(c.FORMAT_ARRAY, function (idx, format) {
                return BI.print(BI.parseDateTime(c.DEFAULT_DATE_STRING + " " + date, c.COMPLETE_COMPARE_FORMAT), format) === date;
            });
        },

        _getTitle: function () {
            var storeValue = this.storeValue || {};
            var date = BI.getDate();
            return BI.print(BI.getDate(date.getFullYear(), 0, 1, storeValue.hour, storeValue.minute, storeValue.second), this._getFormatString());
        },

        _getFormatString: function () {
            return this.options.format || this._const.COMPARE_FORMAT;
        },

        _formatValue: function (v) {
            var now = BI.getDate();
            return BI.isNotEmptyObject(v) ? BI.print(BI.getDate(now.getFullYear(), now.getMonth(), now.getDay(), v.hour, v.minute, v.second), this._getFormatString()) : BI.i18nText("BI-Basic_Unrestricted");
        },

        getKey: function () {
            return this.editor.getValue();
        },

        setValue: function (v) {
            this.storeValue = v;
            this.editor.setValue(this._formatValue(v));
        },

        getValue: function () {
            return this.storeValue;
        }

    });
    BI.shortcut("bi.time_trigger", BI.TimeTrigger);
})();<|endoftext|>


<commit_before>//========================================================================================
// Globals
//========================================================================================
var util = require('util');

var Parser = function() {
    this.varName = '';
    this.type = '';
    this.options = {};
    this.next = null;
    this.head = null;
    this.compiled = null;
};

var PRIMITIVE_TYPES = {
    'UInt8'    : 1,
    'UInt16LE' : 2,
    'UInt16BE' : 2,
    'UInt32LE' : 4,
    'UInt32BE' : 4,
    'Int8'     : 1,
    'Int16LE'  : 2,
    'Int16BE'  : 2,
    'Int32LE'  : 4,
    'Int32BE'  : 4,
    'FloatLE'  : 4,
    'FloatBE'  : 4,
    'DoubleLE' : 8,
    'DoubleBE' : 8
};

var SPECIAL_TYPES = {
    'String'   : null,
    'Array'    : null,
    'Skip'     : null,
    'Choice'   : null
};

// Converts Parser's method names to internal type names
var NAME_MAP = {};
Object.keys(PRIMITIVE_TYPES)
    .concat(Object.keys(SPECIAL_TYPES))
    .forEach(function(type) {
        NAME_MAP[type.toLowerCase()] = type;
    });

//========================================================================================
// Parser builder functions
//========================================================================================

Parser.start = function() {
    return new Parser();
};

Parser.prototype.setNextParser = function(type, varName, options) {
    var parser = new Parser();

    parser.type = NAME_MAP[type];
    parser.varName = varName;
    parser.options = options || parser.options;

    if (this.head) {
        this.head.next = parser;
    } else {
        this.next = parser;
    }
    this.head = parser;

    return this;
};

Object.keys(PRIMITIVE_TYPES)
    .forEach(function(type) {
        Parser.prototype[type.toLowerCase()] = function(varName, options) {
            return this.setNextParser(type.toLowerCase(), varName, options);
        };
    });

Parser.prototype.skip = function(length) {
    return this.setNextParser('skip', '', {length: length});
};

Parser.prototype.string = function(varName, options) {
    if (!options.length) {
        throw new Error('Length option of string is not defined.');
    }
    options.encoding = options.encoding || 'utf8';

    return this.setNextParser('string', varName, options);
};

Parser.prototype.array = function(varName, options) {
    if (!options.length) {
        throw new Error('Length option of array is not defined.');
    }
    if (!options.type) {
        throw new Error('Type option of array is not defined.');
    }
    if (typeof options.type === 'string' && Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.type]) < 0) {
        throw new Error('Specified primitive type "' + options.type + '" is not supported.');
    }

    return this.setNextParser('array', varName, options);
};

Parser.prototype.choice = function(varName, options) {
    if (!options.tag) {
        throw new Error('Tag option of array is not defined.');
    }
    if (!options.choices) {
        throw new Error('Choices option of array is not defined.');
    }
    Object.keys(options.choices).forEach(function(key) {
        if (isNaN(parseInt(key, 10))) {
            throw new Error('Key of choices must be a number.');
        }
        if (!options.choices[key]) {
            throw new Error('Choice ' + key + ' of ' + varName + ' is not defined.');
        }

        if (typeof options.choices[key] === 'string' && Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.choices[key]]) < 0) {
            throw new Error('Specified primitive type "' +  options.choices[key] + '" is not supported.');
        }
    });

    return this.setNextParser('choice', varName, options);
};

Parser.prototype.endianess = function(endianess) {
    if (endianess !== 'little' && endianess !== 'big') {
        throw new Error('Invalid endianess: ' + endianess);
    }
    var re = endianess === 'little' ? /(.+)LE/ : /(.+)BE/;

    Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
        var matched = type.match(re);
        if (matched) {
            var funcName = matched[1].toLowerCase();
            Parser.prototype[funcName] = Parser.prototype[type.toLowerCase()];
        }
    });

    return this;
};

//========================================================================================
// Parsers
//========================================================================================

// Follow the parser chain till the root and start parsing from there
Parser.prototype.parse = function(buffer) {
    if (!Buffer.isBuffer(buffer)) {
        throw new Error('argument "buffer" is not a Buffer object');
    }

    if (this.compiled) {
        return this.compiled(buffer);
    }

    return this.next._parse({
        buffer: buffer,
        iterator: 0,
        vars: {}
    }).vars;
};

// Start parsing from this parser
Parser.prototype._parse = function(ctx) {
    return this['_parse' + this.type](ctx);
};

Parser.prototype.parseNext = function(ctx) {
    return this.next ? this.next._parse(ctx) : ctx;
};

Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
    Parser.prototype['_parse' + type] = function(ctx) {
        ctx.vars[this.varName] = ctx.buffer['read' + type](ctx.iterator);
        ctx.iterator += PRIMITIVE_TYPES[type];

        return this.parseNext(ctx);
    };
});

Parser.prototype._parseSkip = function(ctx) {
    ctx.iterator += this.options.length;

    return this.parseNext(ctx);
};

Parser.prototype.evalField = function(val, ctx) {
    if (typeof val === 'number') {
        return val;
    } else if (typeof val === 'string') {
        if (val in ctx.vars) {
            return ctx.vars[val];
        } else {
            throw new Error('Field ' + val + ' is not yet defined.');
        }
    } else if (typeof val === 'function') {
        return val.call(ctx.vars);
    }
};

Parser.prototype._parseString = function(ctx) {
    var encoding = this.options.encoding;
    var length = this.evalField(this.options.length, ctx);

    ctx.vars[this.varName] = ctx.buffer.toString(encoding, ctx.iterator, ctx.iterator + length);
    ctx.iterator += length;

    return this.parseNext(ctx);
};

Parser.prototype._parseArray = function(ctx) {
    var length = this.evalField(this.options.length, ctx);
    var type = this.options.type;
    var i, data, tmp;

    ctx.vars[this.varName] = [];
    if (typeof type === 'string') {
        for (i = 0; i < length ; i++) {
            tmp = this['_parse' + NAME_MAP[type]].call(
                new Parser(), {
                buffer: ctx.buffer,
                iterator: ctx.iterator,
                vars: {}
            });
            ctx.iterator = tmp.iterator;
            ctx.vars[this.varName].push(tmp.vars['']);
        }
    } else if (type instanceof Parser) {
        for (i = 0; i < length ; i++) {
            tmp = type.next._parse({
                buffer: ctx.buffer,
                iterator: ctx.iterator,
                vars: {}
            });
            ctx.iterator = tmp.iterator;
            ctx.vars[this.varName].push(tmp.vars);
        }
    }

    return this.parseNext(ctx);
};

Parser.prototype._parseChoice = function(ctx) {
    var tag = this.evalField(this.options.tag, ctx);
    var type = this.options.choices[tag];
    var tmp;

    if (typeof type === 'string') {
        tmp = this['_parse' + NAME_MAP[type]].call(
            new Parser(), {
            buffer: ctx.buffer,
            iterator: ctx.iterator,
            vars: {}
        });
        ctx.iterator = tmp.iterator;
        ctx.vars[this.varName] = tmp.vars[''];
    } else if (type instanceof Parser) {
        tmp = type.next._parse({
            buffer: ctx.buffer,
            iterator: ctx.iterator,
            vars: {}
        });
        ctx.iterator = tmp.iterator;
        ctx.vars[this.varName] = tmp.vars;
    }

    return this.parseNext(ctx);
};

//========================================================================================
// JIT code generators
//========================================================================================

Parser.prototype.compile = function() {
    var code =  'var vars = {};\n' +
                'var iterator = 0;\n' +
                this.next.generate({code: '', scopes: []}).code +
                'return vars;';

    console.log();
    console.log(code);

    this.compiled = new Function('buffer', code);
};

// Call code generator for this parser
Parser.prototype.generate = function(ctx) {
    return this['generate' + this.type](ctx);
};

// Recursively call code generators and append results
Parser.prototype.generateNextParser = function(ctx) {
    if (this.next) {
        ctx = this.next.generate(ctx);
    }

    return ctx;
};

Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
    Parser.prototype['generate' + type] = function(ctx) {
        ctx.code += util.format('vars.%s = buffer.read%s(iterator);\n', this.varName, type);
        ctx.code += util.format('iterator += %d;\n', PRIMITIVE_TYPES[type]);

        return this.generateNextParser(ctx);
    };
});

Parser.prototype.generateField = function(val, ctx) {
    if (typeof val === 'number') {
        return val.toString();
    } else if (typeof val === 'string') {
        return util.format('vars.%s', val);
    } else if (typeof val === 'function') {
        return util.format('(%s).call(vars)', val);
    }
};

Parser.prototype.generateSkip = function(ctx) {
    var length = this.generateField(this.options.length, ctx);
    ctx.code += util.format('iterator += %s;\n', length);

    return this.generateNextParser(ctx);
};

Parser.prototype.generateString = function(ctx) {
    ctx.code += util.format('vars.%s = buffer.toString(\'%s\', iterator, iterator + %s);\n',
                                this.varName,
                                this.options.encoding,
                                this.generateField(this.options.length, ctx)
                            );
    ctx.code += util.format('iterator += %s;\n', this.generateField(this.options.length, ctx));

    return this.generateNextParser(ctx);
};

Parser.prototype.generateArray = function(ctx) {
    var length = this.generateField(this.options.length, ctx);
    var type = this.options.type;

    if (typeof type === 'string') {
        ctx.code += util.format('vars.%s = [];\n', this.varName);
        ctx.code += util.format('for (var i = 0; i < %s; i++) {\n', length);
        ctx.code += util.format('\tvars.%s.push(buffer.read%s(iterator));\n', this.varName, NAME_MAP[type]);
        ctx.code += util.format('\titerator += %d;\n', PRIMITIVE_TYPES[NAME_MAP[type]]);
        ctx.code += '}\n';
    } else if (type instanceof Parser) {
        for (i = 0; i < length ; i++) {
            ctx = type.next._parse({
                buffer: context.buffer,
                iterator: context.iterator,
                vars: {}
            });
            context.iterator = ctx.iterator;
            context.vars[this.varName].push(ctx.vars);
        }
    }
    return this.generateNextParser(ctx);
};

//========================================================================================
// Utilities
//========================================================================================

Parser.prototype.sizeOf = function() {
    var size = NaN;

    if (Object.keys(PRIMITIVE_TYPES).indexOf(this.type) >= 0) {
        size = PRIMITIVE_TYPES[this.type];

    // if this is a fixed length string
    } else if (this.type === 'String' && typeof this.options.length === 'number') {
        size = this.options.length;

    // if this is a fixed length array
    } else if (this.type === 'Array' && typeof this.options.length === 'number') {
        var elementSize = NaN;
        if (typeof this.options.type === 'string'){
            elementSize = PRIMITIVE_TYPES[NAME_MAP[this.options.type]];
        } else if (this.options.type instanceof Parser) {
            elementSize = this.options.type.sizeOf();
        }
        size = this.options.length * elementSize;

    // if this a skip
    } else if (this.type === 'Skip') {
        size = this.options.length;

    } else if (!this.type) {
        size = 0;
    }

    if (this.next) {
        size += this.next.sizeOf();
    }

    return size;
};

//========================================================================================
// Exports
//========================================================================================

// TODO skip should be also variable length
// TODO nestable arrays

exports.Parser = Parser;
<commit_msg>Generate proper variable accessors according to ctx.scopes<commit_after>//========================================================================================
// Globals
//========================================================================================
var util = require('util');

var Parser = function() {
    this.varName = '';
    this.type = '';
    this.options = {};
    this.next = null;
    this.head = null;
    this.compiled = null;
};

var PRIMITIVE_TYPES = {
    'UInt8'    : 1,
    'UInt16LE' : 2,
    'UInt16BE' : 2,
    'UInt32LE' : 4,
    'UInt32BE' : 4,
    'Int8'     : 1,
    'Int16LE'  : 2,
    'Int16BE'  : 2,
    'Int32LE'  : 4,
    'Int32BE'  : 4,
    'FloatLE'  : 4,
    'FloatBE'  : 4,
    'DoubleLE' : 8,
    'DoubleBE' : 8
};

var SPECIAL_TYPES = {
    'String'   : null,
    'Array'    : null,
    'Skip'     : null,
    'Choice'   : null
};

// Converts Parser's method names to internal type names
var NAME_MAP = {};
Object.keys(PRIMITIVE_TYPES)
    .concat(Object.keys(SPECIAL_TYPES))
    .forEach(function(type) {
        NAME_MAP[type.toLowerCase()] = type;
    });

//========================================================================================
// Parser builder functions
//========================================================================================

Parser.start = function() {
    return new Parser();
};

Parser.prototype.setNextParser = function(type, varName, options) {
    var parser = new Parser();

    parser.type = NAME_MAP[type];
    parser.varName = varName;
    parser.options = options || parser.options;

    if (this.head) {
        this.head.next = parser;
    } else {
        this.next = parser;
    }
    this.head = parser;

    return this;
};

Object.keys(PRIMITIVE_TYPES)
    .forEach(function(type) {
        Parser.prototype[type.toLowerCase()] = function(varName, options) {
            return this.setNextParser(type.toLowerCase(), varName, options);
        };
    });

Parser.prototype.skip = function(length) {
    return this.setNextParser('skip', '', {length: length});
};

Parser.prototype.string = function(varName, options) {
    if (!options.length) {
        throw new Error('Length option of string is not defined.');
    }
    options.encoding = options.encoding || 'utf8';

    return this.setNextParser('string', varName, options);
};

Parser.prototype.array = function(varName, options) {
    if (!options.length) {
        throw new Error('Length option of array is not defined.');
    }
    if (!options.type) {
        throw new Error('Type option of array is not defined.');
    }
    if (typeof options.type === 'string' && Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.type]) < 0) {
        throw new Error('Specified primitive type "' + options.type + '" is not supported.');
    }

    return this.setNextParser('array', varName, options);
};

Parser.prototype.choice = function(varName, options) {
    if (!options.tag) {
        throw new Error('Tag option of array is not defined.');
    }
    if (!options.choices) {
        throw new Error('Choices option of array is not defined.');
    }
    Object.keys(options.choices).forEach(function(key) {
        if (isNaN(parseInt(key, 10))) {
            throw new Error('Key of choices must be a number.');
        }
        if (!options.choices[key]) {
            throw new Error('Choice ' + key + ' of ' + varName + ' is not defined.');
        }

        if (typeof options.choices[key] === 'string' && Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.choices[key]]) < 0) {
            throw new Error('Specified primitive type "' +  options.choices[key] + '" is not supported.');
        }
    });

    return this.setNextParser('choice', varName, options);
};

Parser.prototype.endianess = function(endianess) {
    if (endianess !== 'little' && endianess !== 'big') {
        throw new Error('Invalid endianess: ' + endianess);
    }
    var re = endianess === 'little' ? /(.+)LE/ : /(.+)BE/;

    Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
        var matched = type.match(re);
        if (matched) {
            var funcName = matched[1].toLowerCase();
            Parser.prototype[funcName] = Parser.prototype[type.toLowerCase()];
        }
    });

    return this;
};

//========================================================================================
// Parsers
//========================================================================================

// Follow the parser chain till the root and start parsing from there
Parser.prototype.parse = function(buffer) {
    if (!Buffer.isBuffer(buffer)) {
        throw new Error('argument "buffer" is not a Buffer object');
    }

    if (this.compiled) {
        return this.compiled(buffer);
    }

    return this.next._parse({
        buffer: buffer,
        iterator: 0,
        vars: {}
    }).vars;
};

// Start parsing from this parser
Parser.prototype._parse = function(ctx) {
    return this['_parse' + this.type](ctx);
};

Parser.prototype.parseNext = function(ctx) {
    return this.next ? this.next._parse(ctx) : ctx;
};

Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
    Parser.prototype['_parse' + type] = function(ctx) {
        ctx.vars[this.varName] = ctx.buffer['read' + type](ctx.iterator);
        ctx.iterator += PRIMITIVE_TYPES[type];

        return this.parseNext(ctx);
    };
});

Parser.prototype._parseSkip = function(ctx) {
    ctx.iterator += this.options.length;

    return this.parseNext(ctx);
};

Parser.prototype.evalField = function(val, ctx) {
    if (typeof val === 'number') {
        return val;
    } else if (typeof val === 'string') {
        if (val in ctx.vars) {
            return ctx.vars[val];
        } else {
            throw new Error('Field ' + val + ' is not yet defined.');
        }
    } else if (typeof val === 'function') {
        return val.call(ctx.vars);
    }
};

Parser.prototype._parseString = function(ctx) {
    var encoding = this.options.encoding;
    var length = this.evalField(this.options.length, ctx);

    ctx.vars[this.varName] = ctx.buffer.toString(encoding, ctx.iterator, ctx.iterator + length);
    ctx.iterator += length;

    return this.parseNext(ctx);
};

Parser.prototype._parseArray = function(ctx) {
    var length = this.evalField(this.options.length, ctx);
    var type = this.options.type;
    var i, data, tmp;

    ctx.vars[this.varName] = [];
    if (typeof type === 'string') {
        for (i = 0; i < length ; i++) {
            tmp = this['_parse' + NAME_MAP[type]].call(
                new Parser(), {
                buffer: ctx.buffer,
                iterator: ctx.iterator,
                vars: {}
            });
            ctx.iterator = tmp.iterator;
            ctx.vars[this.varName].push(tmp.vars['']);
        }
    } else if (type instanceof Parser) {
        for (i = 0; i < length ; i++) {
            tmp = type.next._parse({
                buffer: ctx.buffer,
                iterator: ctx.iterator,
                vars: {}
            });
            ctx.iterator = tmp.iterator;
            ctx.vars[this.varName].push(tmp.vars);
        }
    }

    return this.parseNext(ctx);
};

Parser.prototype._parseChoice = function(ctx) {
    var tag = this.evalField(this.options.tag, ctx);
    var type = this.options.choices[tag];
    var tmp;

    if (typeof type === 'string') {
        tmp = this['_parse' + NAME_MAP[type]].call(
            new Parser(), {
            buffer: ctx.buffer,
            iterator: ctx.iterator,
            vars: {}
        });
        ctx.iterator = tmp.iterator;
        ctx.vars[this.varName] = tmp.vars[''];
    } else if (type instanceof Parser) {
        tmp = type.next._parse({
            buffer: ctx.buffer,
            iterator: ctx.iterator,
            vars: {}
        });
        ctx.iterator = tmp.iterator;
        ctx.vars[this.varName] = tmp.vars;
    }

    return this.parseNext(ctx);
};

//========================================================================================
// JIT code generators
//========================================================================================

Parser.prototype.compile = function() {
    var code =  'var vars = {};\n' +
                'var iterator = 0;\n' +
                this.next.generate({code: '', scopes: []}).code +
                'return vars;';

    console.log();
    console.log(code);

    this.compiled = new Function('buffer', code);
};

// Call code generator for this parser
Parser.prototype.generate = function(ctx) {
    return this['generate' + this.type](ctx);
};

// Recursively call code generators and append results
Parser.prototype.generateNextParser = function(ctx) {
    if (this.next) {
        ctx = this.next.generate(ctx);
    }

    return ctx;
};

Object.keys(PRIMITIVE_TYPES).forEach(function(type) {
    Parser.prototype['generate' + type] = function(ctx) {
        ctx.code += util.format('vars.%s = buffer.read%s(iterator);\n', this.varName, type);
        ctx.code += util.format('iterator += %d;\n', PRIMITIVE_TYPES[type]);

        return this.generateNextParser(ctx);
    };
});

Parser.prototype.generateVariable = function(ctx, name) {
    return ['vars'].concat(ctx.scopes).concat(name).join('.');
};

Parser.prototype.generateOption = function(ctx, val) {
    if (typeof val === 'number') {
        return val.toString();
    } else if (typeof val === 'string') {
        return this.generateVariable(ctx, val);
    } else if (typeof val === 'function') {
        return util.format('(%s).call(%s)', val, this.generateVariable());
    }
};

Parser.prototype.generateSkip = function(ctx) {
    var length = this.generateOption(ctx, this.options.length);
    ctx.code += util.format('iterator += %s;\n', length);

    return this.generateNextParser(ctx);
};

Parser.prototype.generateString = function(ctx) {
    ctx.code += util.format('%s = buffer.toString(\'%s\', iterator, iterator + %s);\n',
                                this.generateVariable(ctx, this.varName),
                                this.options.encoding,
                                this.generateOption(ctx, this.options.length)
                            );
    ctx.code += util.format('iterator += %s;\n', this.generateOption(ctx, this.options.length));

    return this.generateNextParser(ctx);
};

Parser.prototype.generateArray = function(ctx) {
    var length = this.generateOption(ctx, this.options.length);
    var type = this.options.type;
    var tmp;

    if (typeof type === 'string') {
        ctx.code += util.format('%s = [];\n', this.generateVariable(ctx, this.varName));
        ctx.code += util.format('for (var i = 0; i < %s; i++) {\n', length);
        ctx.code += util.format('\t%s.push(buffer.read%s(iterator));\n', this.generateVariable(ctx, this.varName), NAME_MAP[type]);
        ctx.code += util.format('\titerator += %d;\n', PRIMITIVE_TYPES[NAME_MAP[type]]);
        ctx.code += '}\n';
    } else if (type instanceof Parser) {
    }
    return this.generateNextParser(ctx);
};

//========================================================================================
// Utilities
//========================================================================================

Parser.prototype.sizeOf = function() {
    var size = NaN;

    if (Object.keys(PRIMITIVE_TYPES).indexOf(this.type) >= 0) {
        size = PRIMITIVE_TYPES[this.type];

    // if this is a fixed length string
    } else if (this.type === 'String' && typeof this.options.length === 'number') {
        size = this.options.length;

    // if this is a fixed length array
    } else if (this.type === 'Array' && typeof this.options.length === 'number') {
        var elementSize = NaN;
        if (typeof this.options.type === 'string'){
            elementSize = PRIMITIVE_TYPES[NAME_MAP[this.options.type]];
        } else if (this.options.type instanceof Parser) {
            elementSize = this.options.type.sizeOf();
        }
        size = this.options.length * elementSize;

    // if this a skip
    } else if (this.type === 'Skip') {
        size = this.options.length;

    } else if (!this.type) {
        size = 0;
    }

    if (this.next) {
        size += this.next.sizeOf();
    }

    return size;
};

//========================================================================================
// Exports
//========================================================================================

// TODO skip should be also variable length
// TODO nestable arrays

exports.Parser = Parser;
<|endoftext|>


<commit_before>/*global window:false, setTimeout:true, console:true */

(function(context) {
    'use strict';

    var win = context, doc = win.document;

    var global_instance_name = 'cbinstance';

    /*!
     * contentloaded.js
     *
     * Author: Diego Perini (diego.perini at gmail.com)
     * Summary: cross-browser wrapper for DOMContentLoaded
     * Updated: 20101020
     * License: MIT
     * Version: 1.2
     *
     * URL:
     * http://javascript.nwbox.com/ContentLoaded/
     * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
     *
     */
    // @win window reference
    // @fn function reference
    function contentLoaded(win, fn) {
        var done = false, top = true,
        doc = win.document, root = doc.documentElement,

        add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
        rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
        pre = doc.addEventListener ? '' : 'on',

        init = function(e) {
            if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
            (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
            if (!done && (done = true)) fn.call(win, e.type || e);
        },

        poll = function() {
            try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
            init('poll');
        };

        if (doc.readyState == 'complete') fn.call(win, 'lazy');
        else {
            if (doc.createEventObject && root.doScroll) {
                try { top = !win.frameElement; } catch(e) { }
                if (top) poll();
            }
            doc[add](pre + 'DOMContentLoaded', init, false);
            doc[add](pre + 'readystatechange', init, false);
            win[add](pre + 'load', init, false);
        }
    }

    var Cookies = {
        get: function (key) {
            return decodeURIComponent(doc.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null;
        },
        set: function (key, val, end, path, domain, secure) {
            if (!key || /^(?:expires|max\-age|path|domain|secure)$/i.test(key)) {
                return false;
            }
            var expires = '';
            if (end) {
                switch (end.constructor) {
                    case Number:
                        expires = end === Infinity ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : '; max-age=' + end;
                        break;
                    case String:
                        expires = '; expires=' + end;
                    break;
                    case Date:
                        expires = '; expires=' + end.toUTCString();
                    break;
                }
            }
            doc.cookie = encodeURIComponent(key) + '=' + encodeURIComponent(val) + expires + (domain ? '; domain=' + domain : '') + (path ? '; path=' + path : '') + (secure ? '; secure' : '');
            return true;
        },
        has: function (key) {
            return (new RegExp('(?:^|;\\s*)' + encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=')).test(doc.cookie);
        },
        remove: function (key, path, domain) {
            if (!key || !this.has(key)) { return false; }
            doc.cookie = encodeURIComponent(key) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT' + ( domain ? '; domain=' + domain : '') + ( path ? '; path=' + path : '');
            return true;
        }
    };

    var Utils = {

        // merge objects and whatnot
        merge: function(){
            var obj = {},
                i = 0,
                al = arguments.length,
                key;
            if (0 === al) {
                return obj;
            }
            for (; i < al; i++) {
                for (key in arguments[i]) {
                    if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
                        obj[key] = arguments[i][key];
                    }
                }
            }
            return obj;
        },

        str2bool: function(str) {
            str = '' + str;
            switch (str.toLowerCase()) {
                case 'false':
                case 'no':
                case '0':
                case '':
                    return false;
                default:
                    return true;
                }
        },

        fade_in: function(el) {
            if (el.style.opacity < 1) {
                el.style.opacity = (parseFloat(el.style.opacity) + 0.05).toFixed(2);
                win.setTimeout(function(){
                    Utils.fade_in(el);
                }, 50);
            }
        },

        get_data_attribs: function(script) {
            var data = {};
            if (Object.prototype.hasOwnProperty.call(script, 'dataset')) {
                data = script.dataset;
            } else {
                var attribs = script.attributes;
                var key;
                for (key in attribs) {
                    if (Object.prototype.hasOwnProperty.call(attribs, key)) {
                        var attr = attribs[key];
                        if (/^data-/.test(attr.name)) {
                            var camelized = Utils.camelize(attr.name.substr(5));
                            data[camelized] = attr.value;
                        }
                    }
                }
            }
            return data;
        },

        /**
        * "Standardizes" the options keys by converting and removing
        * any potential "dashed-property-name" into "dashedPropertyName".
        * In case both are present, the dashedPropertyName wins.
        */
        normalize_keys: function(options_object) {
            var camelized = {};
            for (var key in options_object) {
                if (Object.prototype.hasOwnProperty.call(options_object, key)) {
                    var camelized_key = Utils.camelize(key);
                    // TODO: could this break for "falsy" values within options_object?
                    // avoiding "dashed-property-name" overriding a potentially existing "dashedPropertyName"
                    camelized[camelized_key] = options_object[camelized_key] ? options_object[camelized_key] : options_object[key];
                }
            }
            return camelized;
        },

        camelize: function(str) {
            var separator = '-',
                match = str.indexOf(separator);
            while (match != -1) {
                var last = (match === (str.length - 1)),
                    next = last ? '' : str[match + 1],
                    upnext = next.toUpperCase(),
                    sep_substr =  last ? separator : separator + next;
                str = str.replace(sep_substr, upnext);
                match = str.indexOf(separator);
            }
            return str;
        },

        find_script_by_id: function(id) {
            var scripts = doc.getElementsByTagName('script');
            for (var i = 0, l = scripts.length; i < l; i++) {
                if (id === scripts[i].id) {
                    return scripts[i];
                }
            }
            return null;
        }

    };

    var script_el_invoker = Utils.find_script_by_id('cookiebanner');

    var Cookiebanner = context.Cookiebanner = function(opts) {
        this.init(opts);
    };

    Cookiebanner.prototype = {

        // for testing stuff from the outside mostly
        cookiejar: Cookies,

        init: function(opts) {

            this.inserted = false;
            this.closed = false;
            this.test_mode = false; // TODO: implement

            var default_text = 'We use cookies to enhance your experience. ' +
                'By continuing to visit this site you agree to our use of cookies.';
            var default_link = 'Learn more';

            this.default_options = {
                // autorun: true,
                cookie: 'cookiebanner-accepted',
                closeText: '✖',
                cookiePath: '/',
                debug: false,
                expires: Infinity,
                zindex: 255,
                mask: false,
                maskOpacity: 0.5,
                maskBackground: '#000',
                height: 'auto',
                minHeight: '21px',
                bg: '#000',
                fg: '#ddd',
                link: '#aaa',
                position: 'bottom',
                message: default_text,
                linkmsg: default_link,
                moreinfo: 'http://aboutcookies.org',
                effect: null,
                fontSize: '14px',
                fontFamily: 'arial, sans-serif',
                instance: global_instance_name,
                textAlign: 'center',
                acceptOnScroll: false
            };

            this.options = this.default_options;
            this.script_el = script_el_invoker;

            if (this.script_el) {
                var data_options = Utils.get_data_attribs(this.script_el);
                this.options = Utils.merge(this.options, data_options);
            }

            // allowing opts passed to the ctor to override everything
            if (opts) {
                // mimics the "data-option-name" HTML attribute becoming
                // this.options.optionName
                opts = Utils.normalize_keys(opts);
                this.options = Utils.merge(this.options, opts);
            }

            // allows customizing the global instance name via options too
            global_instance_name = this.options.instance;

            // TODO: parse/validate other options that can benefit
            this.options.zindex = parseInt(this.options.zindex, 10);
            this.options.mask = Utils.str2bool(this.options.mask);

            // check for a possible global callback specified as a string
            if ('string' === typeof this.options.expires) {
                if ('function' === typeof context[this.options.expires]) {
                    this.options.expires = context[this.options.expires];
                }
            }

            // check if expires is a callback
            if ('function' === typeof this.options.expires) {
                // TODO: this might not always be as simple as this
                this.options.expires = this.options.expires();
            }

            // Proceed with our plans only if we're invoked via a


<commit_before>/**
 *  Copyright 2012 Rackspace
 *
 *  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.
 *
 */

var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var util = require('util');
var querystring = require('querystring');

var sprintf = require('sprintf').sprintf;
var logmagic = require('logmagic');
var log = require('logmagic').local('service-registry-client');
var KeystoneClient = require('keystone-client').KeystoneClient;
var misc = require('rackspace-shared-utils/lib/misc');
var errors = require('rackspace-shared-utils/lib/errors');
var request = require('rackspace-shared-utils/lib/request');

var utils = require('./utils');

/**
 * Default API endpoint url.
 */
var DEFAULT_URL = 'https://dfw.registry.api.rackspacecloud.com/v1.0';

/**
 * How many times to retry a request if API server returns 401 aka token has
 * potentially expired.
 */
var MAX_401_RETRY_COUNT = 1;

/**
 * Library version.
 */
var VERSION = JSON.parse(fs.readFileSync(path.join(__dirname,
                                         '../package.json'))).version;

/**
 * User-agent header sent with every request.
 */
var USER_AGENT = 'node-service-registry-client/v' + VERSION;

/**
 * @constructor
 * @param {String} username API username.
 * @param {String} apiKey API key.
 * @param {?Object} options Options object with the following keys:
 * - url - full API url including a version/
 * - authUrl - auth api url.
 * - debug - true to enable debug mode and print log messages.
 * - raw - pass raw response to the function callback instead of parsing it.
 */
function BaseClient(username, apiKey, region, options) {
  region = region || 'us';
  options = options || {};

  this._url = options.url || DEFAULT_URL;

  if (this._url.charAt(this._url.length - 1) === '/') {
    this._url = this._url.slice(0, this._url.length - 1);
  }

  this._username = username;
  this._apiKey = apiKey;
  this._region = region;
  this._debug = options.debug || false;
  this._options = options;
  this._authUrl = utils.getAuthUrl(options, region);

  if (options.keystoneClient) {
    this._client = options.keystoneClient;
  }
  else {
    this._client = new KeystoneClient(this._authUrl, {'username': username,
                                                      'apiKey': apiKey,
                                                      'cacheTokenFor': 600});
  }

  if (this._debug) {
    logmagic.route('service-registry-client.*', logmagic.DEBUG, 'console');
    this._client.on('log', function(level, msg, obj) {
      log[level](msg, obj);
    });
  }
  else {
    logmagic.registerSink('null', function() {});
    logmagic.route('__root__', logmagic.DEBUG, 'null');
  }
}

util.inherits(BaseClient, EventEmitter);

BaseClient.prototype._request = function(path, method, payload, options, callback) {
  method = method || 'GET';
  payload = payload || null;
  options = options || {};
  var self = this, parseJson = !this._options.raw, retryCount = 0;

  function performRequest(refreshAuthToken) {
    self._client.getTenantIdAndToken({'refreshAuthToken': refreshAuthToken}, function(err, result) {
      var extraHeaders = options.headers || {}, expectedStatusCode = options.expectedStatusCode,
          qs = options.queryString || {}, reqOptions, defaultHeaders, url, headers, curlCmd,
          baseUrl = self._url, data = payload;

      if (err) {
        callback(err);
        return;
      }

      baseUrl += '/' + result.tenantId;

      defaultHeaders = {
        'X-Auth-Token': result.token,
        'User-Agent': USER_AGENT,
        'Content-Type': 'application/json'
      };

      if (data) {
        data = JSON.stringify(data);
      }

      headers = misc.merge(defaultHeaders, extraHeaders);
      url = sprintf('%s%s', baseUrl, path);
      qs = querystring.stringify(qs);

      if (qs) {
        url += '?' + qs;
      }

      curlCmd = request.buildCurlCommand(url, method, headers, payload);
      log.tracef('curl command: ${cmd}', {'cmd': curlCmd});
      reqOptions = {
        'expected_status_codes': [expectedStatusCode],
        'return_response': true,
        'parse_json': parseJson,
        'headers': headers,
        'persistent': options.persistent
      };

      request.request(url, method, data, reqOptions, function(err) {
        if ((err instanceof errors.UnexpectedStatusCodeError) && (err.statusCode === 401) && (retryCount < MAX_401_RETRY_COUNT)) {
          retryCount++;
          performRequest(true);
          return;
        }

        callback.apply(null, arguments);
      });
    });
  }

  performRequest(false);
};

BaseClient.prototype._getIdFromUrl = function(url) {
  var split, id;

  split = url.split('/');
  id = split[split.length - 1];
  return id;
};

BaseClient.prototype._list = function(path, options, callback) {
  var response, self = this, nextMarker;
  options = options || {};
  options.queryString = options.queryString || {};
  options.expectedStatusCode = 200;

  if (options.marker) {
    options.queryString.marker = options.marker;
  }

  if (options.limit) {
    options.queryString.limit = options.limit;
  }

  this._request(path, 'GET', null, options, function(err, res) {
    if (err) {
      callback(err);
      return;
    }

    response = self._options.raw ? res : res.body.values;
    if (res.body && res.body.metadata && res.body.metadata.next_marker) {
      nextMarker = res.body.metadata.next_marker;
    }
    callback(null, response, nextMarker);
  });
};

BaseClient.prototype._get = function(path, options, callback) {
  var response, self = this;
  options = options || {};
  options.expectedStatusCode = 200;

  this._request(path, 'GET', null, options, function(err, res) {
    if (err) {
      callback(err);
      return;
    }

    response = self._options.raw ? res : res.body;
    callback(null, response);
  });
};

BaseClient.prototype._create = function(path, payload, options, callback) {
  options = options || {};
  options.expectedStatusCode = 201;
  this._request(path, 'POST', payload, options, function(err, res) {
    callback(err, res);
  });
};

BaseClient.prototype._update = function(path, payload, options, callback) {
  options = options || {};
  options.expectedStatusCode = 204;
  this._request(path, 'PUT', payload, options, function(err, res) {
    callback(err, res);
  });
};

BaseClient.prototype._remove = function(path, options, callback) {
  options = options || {};
  options.expectedStatusCode = 204;
  this._request(path, 'DELETE', null, options, function(err, res) {
    callback(err);
  });
};

exports.BaseClient = BaseClient;
<commit_msg>Make sure we don't add 'log' listener more than once.<commit_after>/**
 *  Copyright 2012 Rackspace
 *
 *  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.
 *
 */

var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var util = require('util');
var querystring = require('querystring');

var sprintf = require('sprintf').sprintf;
var logmagic = require('logmagic');
var log = require('logmagic').local('service-registry-client');
var KeystoneClient = require('keystone-client').KeystoneClient;
var misc = require('rackspace-shared-utils/lib/misc');
var errors = require('rackspace-shared-utils/lib/errors');
var request = require('rackspace-shared-utils/lib/request');

var utils = require('./utils');

/**
 * Default API endpoint url.
 */
var DEFAULT_URL = 'https://dfw.registry.api.rackspacecloud.com/v1.0';

/**
 * How many times to retry a request if API server returns 401 aka token has
 * potentially expired.
 */
var MAX_401_RETRY_COUNT = 1;

/**
 * Library version.
 */
var VERSION = JSON.parse(fs.readFileSync(path.join(__dirname,
                                         '../package.json'))).version;

/**
 * User-agent header sent with every request.
 */
var USER_AGENT = 'node-service-registry-client/v' + VERSION;

/**
 * @constructor
 * @param {String} username API username.
 * @param {String} apiKey API key.
 * @param {?Object} options Options object with the following keys:
 * - url - full API url including a version/
 * - authUrl - auth api url.
 * - debug - true to enable debug mode and print log messages.
 * - raw - pass raw response to the function callback instead of parsing it.
 */
function BaseClient(username, apiKey, region, options) {
  region = region || 'us';
  options = options || {};

  this._url = options.url || DEFAULT_URL;

  if (this._url.charAt(this._url.length - 1) === '/') {
    this._url = this._url.slice(0, this._url.length - 1);
  }

  this._username = username;
  this._apiKey = apiKey;
  this._region = region;
  this._debug = options.debug || false;
  this._options = options;
  this._authUrl = utils.getAuthUrl(options, region);

  if (options.keystoneClient) {
    this._client = options.keystoneClient;
  }
  else {
    this._client = new KeystoneClient(this._authUrl, {'username': username,
                                                      'apiKey': apiKey,
                                                      'cacheTokenFor': 600});
  }

  if (this._debug) {
    logmagic.route('service-registry-client.*', logmagic.DEBUG, 'console');

    // If _client is re-used this could cause 'log' listener to be added
    // multiple times
    if (this._client.listeners('log').length === 0) {
      this._client.on('log', function(level, msg, obj) {
        log[level](msg, obj);
      });
    }
  }
  else {
    logmagic.registerSink('null', function() {});
    logmagic.route('__root__', logmagic.DEBUG, 'null');
  }
}

util.inherits(BaseClient, EventEmitter);

BaseClient.prototype._request = function(path, method, payload, options, callback) {
  method = method || 'GET';
  payload = payload || null;
  options = options || {};
  var self = this, parseJson = !this._options.raw, retryCount = 0;

  function performRequest(refreshAuthToken) {
    self._client.getTenantIdAndToken({'refreshAuthToken': refreshAuthToken}, function(err, result) {
      var extraHeaders = options.headers || {}, expectedStatusCode = options.expectedStatusCode,
          qs = options.queryString || {}, reqOptions, defaultHeaders, url, headers, curlCmd,
          baseUrl = self._url, data = payload;

      if (err) {
        callback(err);
        return;
      }

      baseUrl += '/' + result.tenantId;

      defaultHeaders = {
        'X-Auth-Token': result.token,
        'User-Agent': USER_AGENT,
        'Content-Type': 'application/json'
      };

      if (data) {
        data = JSON.stringify(data);
      }

      headers = misc.merge(defaultHeaders, extraHeaders);
      url = sprintf('%s%s', baseUrl, path);
      qs = querystring.stringify(qs);

      if (qs) {
        url += '?' + qs;
      }

      curlCmd = request.buildCurlCommand(url, method, headers, payload);
      log.tracef('curl command: ${cmd}', {'cmd': curlCmd});
      reqOptions = {
        'expected_status_codes': [expectedStatusCode],
        'return_response': true,
        'parse_json': parseJson,
        'headers': headers,
        'persistent': options.persistent
      };

      request.request(url, method, data, reqOptions, function(err) {
        if ((err instanceof errors.UnexpectedStatusCodeError) && (err.statusCode === 401) && (retryCount < MAX_401_RETRY_COUNT)) {
          retryCount++;
          performRequest(true);
          return;
        }

        callback.apply(null, arguments);
      });
    });
  }

  performRequest(false);
};

BaseClient.prototype._getIdFromUrl = function(url) {
  var split, id;

  split = url.split('/');
  id = split[split.length - 1];
  return id;
};

BaseClient.prototype._list = function(path, options, callback) {
  var response, self = this, nextMarker;
  options = options || {};
  options.queryString = options.queryString || {};
  options.expectedStatusCode = 200;

  if (options.marker) {
    options.queryString.marker = options.marker;
  }

  if (options.limit) {
    options.queryString.limit = options.limit;
  }

  this._request(path, 'GET', null, options, function(err, res) {
    if (err) {
      callback(err);
      return;
    }

    response = self._options.raw ? res : res.body.values;
    if (res.body && res.body.metadata && res.body.metadata.next_marker) {
      nextMarker = res.body.metadata.next_marker;
    }
    callback(null, response, nextMarker);
  });
};

BaseClient.prototype._get = function(path, options, callback) {
  var response, self = this;
  options = options || {};
  options.expectedStatusCode = 200;

  this._request(path, 'GET', null, options, function(err, res) {
    if (err) {
      callback(err);
      return;
    }

    response = self._options.raw ? res : res.body;
    callback(null, response);
  });
};

BaseClient.prototype._create = function(path, payload, options, callback) {
  options = options || {};
  options.expectedStatusCode = 201;
  this._request(path, 'POST', payload, options, function(err, res) {
    callback(err, res);
  });
};

BaseClient.prototype._update = function(path, payload, options, callback) {
  options = options || {};
  options.expectedStatusCode = 204;
  this._request(path, 'PUT', payload, options, function(err, res) {
    callback(err, res);
  });
};

BaseClient.prototype._remove = function(path, options, callback) {
  options = options || {};
  options.expectedStatusCode = 204;
  this._request(path, 'DELETE', null, options, function(err, res) {
    callback(err);
  });
};

exports.BaseClient = BaseClient;
<|endoftext|>


<commit_before>var gulp = require('gulp');
var fs = require('fs');
var request = require('request');
var configPath = process.cwd() + '/copy-config.js';
var config = require(configPath).methode;

var _queue = [];
var _output = '';

gulp.task('fetch-methode', function(cb) {
	if (config.story.length) {

		config.imageDirectory = config.imageDirectory || 'assets';

		var next = function(index) {
			// fetch xml
			var base = 'http://prdedit.bostonglobe.com/eom/Boston/Content/';
			var url = base + config.section + '/Stories/' + config.story[index].slug + '.xml';
			console.log('fetching', url);
			request(url, function(error, response, body) {
				// did we get a valid response?
				if (!error && response.statusCode === 200) {
					// extract the html between content tags
					var content = body.match(/(<content>)([\s\S]*)(<\/content>)/)[2];

					// replace all the weird bits and bobs
					content = deMethodeify(content);

					// insert graphic templates
					content = content.replace(/<annotation.*(graphic:)(.*)<\/annotation>(.|[\r\n]+).*(<\/p>)/g, function(a, b, c) {
						return '
\n{{> graphic/graphic-' + c + '}}';
					});

					// replace photo tags with desired markup
					content = content.replace(/<photogrp-inline (.*?)>([\S\s]*?)<\/photogrp-inline>/g, function(a, b, c) {
						return createImageMarkup(c);
					});

					appendOutput(content, config.story[index]);

				} else {
					// http error. log and quit.
					console.log('error');
					console.log(JSON.stringify(error, null, 4));
					advance(index);
				}

				advance(index);
			});
		};

		var advance = function(index) {
			index++;
			if (index < config.story.length) {
				next(index);
			} else {
				fs.writeFileSync('src/html/partials/graphic/graphic.hbs', _output);
				downloadImages(cb);
			}
		};

		next(0);
	} else {
		console.error('No methode story');
		cb();
	}
});

function deMethodeify(content) {
	// remove notes
	content = content.replace(//g, '');
	content = content.replace(/
/g, '');

	// remove channel...
	content = content.replace(//g, '');

	// replace *** with hr
	content = content.replace(/\
 *\*\*\* *\<\/p\>/g, '
');

	// bold tag
	content = content.replace(//g, '');
	content = content.replace(/<\/b>/g, '');

	return content;
}

function createImageMarkup(c) {
	// match src
	var src = c.match(/<photo-inline (.*?) fileref="(.*?)" (.*?)>([\S\s]*?)<\/photo-inline>/)[2];

	// match caption and remove
	var caption = c.match(/
([\S\s]*?)<\/caption>/)[2];
	caption = caption.replace(/\
|\<\/p\>/g,'').trim();

	// match credit and remove
	var credit = c.match(/<credit (.*?)>([\S\s]*?)<\/credit>/)[2];
	credit = credit.replace(/\
|\<\/p\>/g,'').trim();

	var imgPath = src.split('?')[0];

	var figure = createFigure({
		lib: config.imageLibrary,
		imgPath: imgPath,
		caption: caption,
		credit: credit
	});
	return figure;
}

function createFigure(params) {
	var imgFull = params.imgPath.substr(params.imgPath.lastIndexOf('/') + 1);
	var imgSplit = imgFull.split('.');
	var name = imgSplit[0];
	var extension = imgSplit[1];

	// populate download queue
	var imgSizes = {
		small: 585,
		medium: 1200,
		large: 1920
	};

	for (var sz in imgSizes) {
		_queue.push({
			url: 'http://prdedit.bostonglobe.com/rf/image_' + imgSizes[sz] + 'w' + params.imgPath,
			src: name + '_' + imgSizes[sz] + '.' + extension
		});
	}

	// start generating markup
	var src;
	var figure = '
';

	if (params.lib === 'imager') {
		src = config.imageDirectory + '/' + name + '_{width}' + '.' + extension;
		figure += '';
	} else if (params.lib === 'picturefill') {
		figure += 'TODO';
	} else {
		src = config.imageDirectory + '/' + name + '_' + imgSizes.medium + '.' + extension;
		figure += '';
	}

	figure += '' + params.credit + '';
	figure += '
' + params.caption + '
';
	figure += '
';

	return figure;
}

function appendOutput(content, story) {
	_output += content;
}

function downloadImages(cb) {
	var next = function(index) {
		var path = 'src/' + config.imageDirectory + '/' + _queue[index].src;
		try {
			var exists = fs.lstatSync(path);
			advance(index);
		} catch (e) {
			console.log('downloading', _queue[index].url);
			request(_queue[index].url, {encoding: 'binary'}, function(error, response, body) {
				if (error) {
					console.error(error);
					advance(index);
				} else {
					fs.writeFile(path, body, 'binary', function(err) {
						if (err) { console.error(err); }
						advance(index);
					});
				}
			});
		}
	};

	var advance = function(index) {
		index++;
		if (index < _queue.length) {
			next(index);
		} else {
			cb();
		}
	};

	if (_queue.length) {
		next(0);
	}
}
<commit_msg>picturefill option<commit_after>var gulp = require('gulp');
var fs = require('fs');
var request = require('request');
var configPath = process.cwd() + '/copy-config.js';
var config = require(configPath).methode;

var _queue = [];
var _output = '';

gulp.task('fetch-methode', function(cb) {
	if (config.story.length) {

		config.imageDirectory = config.imageDirectory || 'assets';

		var next = function(index) {
			// fetch xml
			var base = 'http://prdedit.bostonglobe.com/eom/Boston/Content/';
			var url = base + config.section + '/Stories/' + config.story[index].slug + '.xml';
			console.log('fetching', url);
			request(url, function(error, response, body) {
				// did we get a valid response?
				if (!error && response.statusCode === 200) {
					// extract the html between content tags
					var content = body.match(/(<content>)([\s\S]*)(<\/content>)/)[2];

					// replace all the weird bits and bobs
					content = deMethodeify(content);

					// insert graphic templates
					content = content.replace(/<annotation.*(graphic:)(.*)<\/annotation>(.|[\r\n]+).*(<\/p>)/g, function(a, b, c) {
						return '
\n{{> graphic/graphic-' + c + '}}';
					});

					// replace photo tags with desired markup
					content = content.replace(/<photogrp-inline (.*?)>([\S\s]*?)<\/photogrp-inline>/g, function(a, b, c) {
						return createImageMarkup(c);
					});

					appendOutput(content, config.story[index]);

				} else {
					// http error. log and quit.
					console.log('error');
					console.log(JSON.stringify(error, null, 4));
					advance(index);
				}

				advance(index);
			});
		};

		var advance = function(index) {
			index++;
			if (index < config.story.length) {
				next(index);
			} else {
				fs.writeFileSync('src/html/partials/graphic/graphic.hbs', _output);
				downloadImages(cb);
			}
		};

		next(0);
	} else {
		console.error('No methode story');
		cb();
	}
});

function deMethodeify(content) {
	// remove notes
	content = content.replace(//g, '');
	content = content.replace(/
/g, '');

	// remove channel...
	content = content.replace(//g, '');

	// replace *** with hr
	content = content.replace(/\
 *\*\*\* *\<\/p\>/g, '
');

	// bold tag
	content = content.replace(//g, '');
	content = content.replace(/<\/b>/g, '');

	return content;
}

function createImageMarkup(c) {
	// match src
	var src = c.match(/<photo-inline (.*?) fileref="(.*?)" (.*?)>([\S\s]*?)<\/photo-inline>/)[2];

	// match caption and remove
	var caption = c.match(/
([\S\s]*?)<\/caption>/)[2];
	caption = caption.replace(/\
|\<\/p\>/g,'').trim();

	// match credit and remove
	var credit = c.match(/<credit (.*?)>([\S\s]*?)<\/credit>/)[2];
	credit = credit.replace(/\
|\<\/p\>/g,'').trim();

	var imgPath = src.split('?')[0];

	var figure = createFigure({
		lib: config.imageLibrary,
		imgPath: imgPath,
		caption: caption,
		credit: credit
	});
	return figure;
}

function createFigure(params) {
	var imgFull = params.imgPath.substr(params.imgPath.lastIndexOf('/') + 1);
	var imgSplit = imgFull.split('.');
	var name = imgSplit[0];
	var extension = imgSplit[1];

	// populate download queue
	var imgSizes = [585,1200,1920];

	for (var i in imgSizes) {
		_queue.push({
			url: 'http://prdedit.bostonglobe.com/rf/image_' + imgSizes[i] + 'w' + params.imgPath,
			src: name + '_' + imgSizes[i] + '.' + extension
		});
	}

	// start generating markup
	var src;
	var figure = '
';

	if (params.lib === 'imager') {

		src = config.imageDirectory + '/' + name + '_{width}' + '.' + extension;
		figure += '';

	} else if (params.lib === 'picturefill') {

		figure += '<picture>';
		figure += '';
		for (var j = imgSizes.length - 1; j > -1; j--) {
			src = config.imageDirectory + '/' + name + '_' + imgSizes[j] + '.' + extension;
			figure += '<source srcset="' + src  + '" ';
			if (j > 0) {
				figure += 'media="(min-width: ' + Math.floor(imgSizes[j-1] / 1.5) + 'px)"';
			} else {
				figure += 'media="(min-width: 1px)"';
			}

			figure += '>';
		}

		figure += '';

		src = config.imageDirectory + '/' + name + '_' + imgSizes[0] + '.' + extension;
		figure += '';
		figure += '</picture>';

	} else {

		src = config.imageDirectory + '/' + name + '_' + imgSizes.medium + '.' + extension;
		figure += '';

	}

	figure += '' + params.credit + '';
	figure += '
' + params.caption + '
';
	figure += '
';

	return figure;
}

function appendOutput(content, story) {
	_output += content;
}

function downloadImages(cb) {
	var next = function(index) {
		var path = 'src/' + config.imageDirectory + '/' + _queue[index].src;
		try {
			var exists = fs.lstatSync(path);
			advance(index);
		} catch (e) {
			console.log('downloading', _queue[index].url);
			request(_queue[index].url, {encoding: 'binary'}, function(error, response, body) {
				if (error) {
					console.error(error);
					advance(index);
				} else {
					fs.writeFile(path, body, 'binary', function(err) {
						if (err) { console.error(err); }
						advance(index);
					});
				}
			});
		}
	};

	var advance = function(index) {
		index++;
		if (index < _queue.length) {
			next(index);
		} else {
			cb();
		}
	};

	if (_queue.length) {
		next(0);
	}
}
<|endoftext|>