<commit_before>$(document).ready(function () {

    initI18N();
    bindEvents();

    function initI18N() {
        var path = getCommonScriptPath();
        var lan = utils.getLanguage();
        i18next
            .use(i18nextXHRBackend)
            .init({
                lng: lan,
                whitelist: ["zh-CN", "en-US"],//语言列表，跟locales下的目录名对应
                ns: "resources",//locales下的json文件名称
                defaultNS: "resources",//locales下的json文件名称
                fallbackLng: "zh-CN",//默认语言
                backend: {
                    loadPath: path + './locales/{{lng}}/{{ns}}.json'
                }
            }, function (err, t) {
                if (window.isSite) {
                    localize();
                }
                jqueryI18next.init(i18next, $);
                $('html').attr("lang", lan);
                $(".nav").localize();//翻译nav下所有的文档
                $(".icl-footer").localize();//翻译footer
                onLoadCallBack(); //设置标题栏当前语言
            });
    }

    function onLoadCallBack() {
        var lan = utils.getLanguage();
        var lang_text = $("[data-lang=" + lan + "]").html() || "中文";
        $('#lang').html(lang_text);

        setCurrentVersion();
        resetCurrentVersionLink();
    }

    //设置头部版本号
    function setCurrentVersion() {
        if (window.isLocal) {
            return;
        }
        var version = getVersion();
        var versionText = version ? "" + version : "";
        $('#version').html(versionText);
    }

    //重置当前版本链接,不带版本号
    function resetCurrentVersionLink() {
        if (!window.version) {
            return;
        }

        var version = window.version;
        version = version.toString();
        $(".icl-nav-version").each(function (key, item) {
            if (item.href) {
                var reg = new RegExp("(.*)\/(" + version + ")(\/.*)");
                var match = item.href.match(reg);
                if (match && match[1] && match[3]) {
                    item.href = match[1] + match[3];
                }
            }
        });
    }

    function bindEvents() {
        $('.icl-header').on('click', '.lang-option', function () {
            var value = $(this).data('lang');
            utils.setLanguage(value);
            $('#lang').html($(this).html());
            i18next.changeLanguage(value);
            if (window.isSite) {
                localize();
                return;
            }
            window.location.reload();
        });
    }

    function localize() {
        var lang = utils.getLanguage();
        var pathname = window.location.pathname.replace("/en/", "/");
        var href = window.location.origin + pathname;
        if (lang === "en-US") {
            var match = pathname.match(/([^\/]*\/){2}([^\/]*)/);
            if (match && match[2]) {
                var versionReg = match[2].match(/dev|^(.*)\.(.*)$/);//匹配版本:dev/9.0.0
                if (versionReg) {
                    // href = window.location.origin + pathname.replace(/([^\/]*\/){1}([^\/]*)/, '$1$2/en');
                    href = window.location.origin + pathname.replace(/([^\/]*\/){2}([^\/]*)/, '/$1$2/en');
                }else if(window.isLocal) {
                    href = window.location.origin + pathname.replace(/(([^\/]*\/){3})([^\/]*)/,'$1$3/en');
                }
            } else if(window.isLocal) {
                href = window.location.origin + pathname.replace(/(([^\/]*\/){3})([^\/]*)/,'$1$3/en')
            }else {
                href = window.location.origin + pathname.replace(/([^\/]*\/){1}([^\/]*)/, '/en/$2');
                //href = window.location.origin + pathname.replace(/([^\/]*\/){1}([^\/]*)/, '/$2/en');
            }

        }
        if ((window.location.origin + window.location.pathname) === href) {
            return;
        }
        window.location = href;
    }

    function getCommonScriptPath() {
        var r = new RegExp("(^|(.*?\\/))(common\.js)(\\?|$)"),
            s = document.getElementsByTagName('script'), relativePath;
        for (var i = 0; i < s.length; i++) {
            var src = s[i].getAttribute('src');
            if (src) {
                var m = src.match(r);
                if (m) {
                    relativePath = m[1] ? m[1].replace("js/", "") : "./";
                    break;
                }
            }
        }
        return relativePath;
    }

});<commit_msg>fix 获取URL中版本号错误问题<commit_after>$(document).ready(function () {

    initI18N();
    bindEvents();

    function initI18N() {
        var path = getCommonScriptPath();
        var lan = utils.getLanguage();
        i18next
            .use(i18nextXHRBackend)
            .init({
                lng: lan,
                whitelist: ["zh-CN", "en-US"],//语言列表，跟locales下的目录名对应
                ns: "resources",//locales下的json文件名称
                defaultNS: "resources",//locales下的json文件名称
                fallbackLng: "zh-CN",//默认语言
                backend: {
                    loadPath: path + './locales/{{lng}}/{{ns}}.json'
                }
            }, function (err, t) {
                if (window.isSite) {
                    localize();
                }
                jqueryI18next.init(i18next, $);
                $('html').attr("lang", lan);
                $(".nav").localize();//翻译nav下所有的文档
                $(".icl-footer").localize();//翻译footer
                onLoadCallBack(); //设置标题栏当前语言
            });
    }

    function onLoadCallBack() {
        var lan = utils.getLanguage();
        var lang_text = $("[data-lang=" + lan + "]").html() || "中文";
        $('#lang').html(lang_text);

        setCurrentVersion();
        resetCurrentVersionLink();
    }

    //设置头部版本号
    function setCurrentVersion() {
        if (window.isLocal) {
            return;
        }
        var version = getVersion();
        var versionText = version ? "" + version : "";
        $('#version').html(versionText);
    }

    function getVersion() {
        var pathname = window.location.pathname.replace("/en/", "/");
        var match = pathname.match(/^\/(dev|example|(?:\d+\.)+\d)\/.*/);//匹配版本:dev|web|9.0.0
        return match && match[1] !== "example" ? match[1] : null;
    }

    //重置当前版本链接,不带版本号
    function resetCurrentVersionLink() {
        if (!window.version) {
            return;
        }

        var version = window.version;
        version = version.toString();
        $(".icl-nav-version").each(function (key, item) {
            if (item.href) {
                var reg = new RegExp("(.*)\/(" + version + ")(\/.*)");
                var match = item.href.match(reg);
                if (match && match[1] && match[3]) {
                    item.href = match[1] + match[3];
                }
            }
        });
    }

    function bindEvents() {
        $('.icl-header').on('click', '.lang-option', function () {
            var value = $(this).data('lang');
            utils.setLanguage(value);
            $('#lang').html($(this).html());
            i18next.changeLanguage(value);
            if (window.isSite) {
                localize();
                return;
            }
            window.location.reload();
        });
    }

    function localize() {
        var lang = utils.getLanguage();
        var pathname = window.location.pathname.replace("/en/", "/");
        var href = window.location.origin + pathname;
        if (lang === "en-US") {
            if (getVersion()) {
                // href = window.location.origin + pathname.replace(/([^\/]*\/){1}([^\/]*)/, '$1$2/en');
                href = window.location.origin + pathname.replace(/([^\/]*\/){2}([^\/]*)/, '/$1$2/en');
            } else if (window.isLocal) {
                href = window.location.origin + pathname.replace(/(([^\/]*\/){3})([^\/]*)/, '$1$3/en')
            } else {
                href = window.location.origin + pathname.replace(/([^\/]*\/){1}([^\/]*)/, '/en/$2');
                //href = window.location.origin + pathname.replace(/([^\/]*\/){1}([^\/]*)/, '/$2/en');
            }

        }
        if ((window.location.origin + window.location.pathname) === href) {
            return;
        }
        window.location = href;
    }

    function getCommonScriptPath() {
        var r = new RegExp("(^|(.*?\\/))(common\.js)(\\?|$)"),
            s = document.getElementsByTagName('script'), relativePath;
        for (var i = 0; i < s.length; i++) {
            var src = s[i].getAttribute('src');
            if (src) {
                var m = src.match(r);
                if (m) {
                    relativePath = m[1] ? m[1].replace("js/", "") : "./";
                    break;
                }
            }
        }
        return relativePath;
    }

});<|endoftext|>


<commit_before>import { isUndefined, keys } from 'underscore';
import PropertyCompositeView from './PropertyCompositeView';
import LayersView from './LayersView';
import CssGenerator from 'code_manager/model/CssGenerator';

const cssGen = new CssGenerator();

export default PropertyCompositeView.extend({
  templateInput() {
    const pfx = this.pfx;
    const ppfx = this.ppfx;
    return `

        <button type="button" id="${pfx}add" data-add-layer>+</button>

    `;
  },

  init() {
    const model = this.model;
    const pfx = this.pfx;
    model.set('stackIndex', null);
    this.events[`click [data-add-layer]`] = 'addLayer';
    this.listenTo(model, 'change:stackIndex', this.indexChanged);
    this.listenTo(model, 'updateValue', this.inputValueChanged);
    this.delegateEvents();
  },

  /**
   * Fired when the target is updated.
   * With detached mode the component will be always empty as its value
   * so we gonna check all props and find if it has any difference
   * */
  targetUpdated(...args) {
    if (!this.model.get('detached')) {
      PropertyCompositeView.prototype.targetUpdated.apply(this, args);
    } else {
      const { status } = this._getTargetData();
      this.setStatus(status);
      this.checkVisibility();
    }

    this.refreshLayers();
  },

  /**
   * Returns the collection of layers
   * @return {Collection}
   */
  getLayers() {
    return this.model.get('layers');
  },

  /**
   * Triggered when another layer has been selected.
   * This allow to move all rendered properties to a new
   * selected layer
   * @param {Event}
   *
   * @return {Object}
   * */
  indexChanged(e) {
    const model = this.model;
    this.getLayers().active(model.get('stackIndex'));
  },

  addLayer() {
    const model = this.model;
    const layers = this.getLayers();
    const properties = model.get('properties').deepClone();
    properties.each(property => property.set('value', ''));
    const layer = layers.add({ properties });

    // In detached mode inputValueChanged will add new 'layer value'
    // to all subprops
    this.inputValueChanged();

    // This will set subprops with a new default values
    model.set('stackIndex', layers.indexOf(layer));
  },

  inputValueChanged() {
    const model = this.model;
    this.elementUpdated();

    // If not detached I'll just put all the values from layers to property
    // eg. background: layer1Value, layer2Value, layer3Value, ...
    if (!model.get('detached')) {
      model.set('value', this.getLayerValues());
    } else {
      model.get('properties').each(prop => prop.trigger('change:value'));
    }
  },

  /**
   * There is no need to handle input update by the property itself,
   * this will be done by layers
   * @private
   */
  setValue() {},

  /**
   * Create value by layers
   * @return string
   * */
  getLayerValues() {
    return this.getLayers().getFullValue();
  },

  _getClassRule(opts = {}) {
    const { em } = this;
    const { skipAdd = 1 } = opts;
    const selected = em.getSelected();
    const targetAlt = em.get('StyleManager').getModelToStyle(selected, {
      skipAdd,
      useClasses: 1
    });
    return targetAlt !== selected && targetAlt;
  },

  /**
   * Return the parent style rule of the passed one
   * @private
   */
  _getParentTarget(target) {
    const { em, model } = this;
    const property = model.get('property');
    const targetsDevice = em
      .get('CssComposer')
      .getAll()
      .filter(rule => rule.selectorsToString() === target.getSelectorsString());
    const map = targetsDevice.reduce((acc, rule) => {
      acc[rule.getAtRule()] = rule;
      return acc;
    }, {});
    const mapSorted = cssGen.sortMediaObject(map);
    const sortedRules = mapSorted.map(item => item.value);
    const currIndex = sortedRules.indexOf(target);
    const rulesToCheck = sortedRules.splice(0, currIndex);
    let result;

    for (let i = rulesToCheck.length - 1; i > -1; i--) {
      const rule = rulesToCheck[i];
      if (rule.getStyle()[property]) {
        // only for not detached
        result = rule;
        break;
      }
    }

    return result;
  },

  /**
   * Refresh layers
   * */
  refreshLayers() {
    let layersObj = [];
    const { model, em } = this;
    const layers = this.getLayers();
    const detached = model.get('detached');
    const property = model.get('property');
    const target = this.getTarget();
    const valueComput = this.getComputedValue();
    const selected = em.getSelected();
    let style, targetAlt, targetAltDevice;

    // With detached layers values will be assigned to their properties
    if (detached) {
      style = target ? target.getStyle() : {};

      // If the style object is empty but the target has a computed value,
      // that means the style might exist in some other place
      if (!keys(style).length && valueComput && selected) {
        // The target is a component but the style is in the class rules
        targetAlt = this._getClassRule();
        style = targetAlt ? targetAlt.getStyle() : 0;
      }

      layersObj = layers.getLayersFromStyle(style);
    } else {
      const valueTrg = this.getTargetValue({ ignoreDefault: 1 });
      let value = valueTrg;

      // Try to check if the style is in another rule
      if (!value && valueComput) {
        // Styles of the same target but with a higher rule
        targetAltDevice = this._getParentTarget(target);

        if (targetAltDevice) {
          value = targetAltDevice.getStyle()[property];
        } else {
          // Computed value is not always reliable due to the browser's CSSOM parser
          // here we try to look for the style in class rules
          targetAlt = this._getClassRule();
          const valueTargetAlt = targetAlt && targetAlt.getStyle()[property];
          targetAltDevice =
            !valueTargetAlt &&
            this._getParentTarget(this._getClassRule({ skipAdd: 0 }));
          const valueTrgAltDvc =
            targetAltDevice && targetAltDevice.getStyle()[property];
          value = valueTargetAlt || valueTrgAltDvc || valueComput;
        }
      }

      value = value == model.getDefaultValue() ? '' : value;
      layersObj = layers.getLayersFromValue(value);
    }

    const toAdd = model.getLayersFromTarget(target) || layersObj;
    layers.reset();
    layers.add(toAdd);
    model.set({ stackIndex: null }, { silent: true });
  },

  getTargetValue(opts = {}) {
    let result = PropertyCompositeView.prototype.getTargetValue.call(
      this,
      opts
    );
    const { detached } = this.model.attributes;

    // It might happen that the browser split properties on CSSOM parse
    if (isUndefined(result) && !detached) {
      result = this.model.getValueFromStyle(this.getTarget().getStyle());
    }

    return result;
  },

  onRender() {
    const self = this;
    const model = this.model;
    const fieldEl = this.el.querySelector('[data-layers-wrapper]');
    const PropertiesView = require('./PropertiesView').default;
    const propsConfig = {
      target: this.target,
      propTarget: this.propTarget,

      // Things to do when a single sub-property is changed
      onChange(el, view, opt) {
        const subModel = view.model;

        if (model.get('detached')) {
          const subProp = subModel.get('property');
          const values = self.getLayers().getPropertyValues(subProp);
          view.updateTargetStyle(values, null, opt);
        } else {
          // Update only if there is an actual update (to avoid changes for computed styles)
          // ps: status is calculated in `targetUpdated` method
          if (model.get('status') == 'updated') {
            const value = model.getFullValue();
            model.set('value', value, opt);
            // Try to remove detached properties
            !value && view.updateTargetStyle(value, null, opt);
          }
        }
      }
    };
    const layers = new LayersView({
      collection: this.getLayers(),
      stackModel: model,
      preview: model.get('preview'),
      config: this.config,
      propsConfig
    }).render().el;

    // Will use it to propogate changes
    new PropertiesView({
      target: this.target,
      collection: this.model.get('properties'),
      stackModel: model,
      config: this.config,
      onChange: propsConfig.onChange,
      propTarget: propsConfig.propTarget
    }).render();

    //model.get('properties')
    fieldEl.appendChild(layers);
  }
});
<commit_msg>Search alternaive device target for detached properties<commit_after>import { isUndefined, keys } from 'underscore';
import PropertyCompositeView from './PropertyCompositeView';
import LayersView from './LayersView';
import CssGenerator from 'code_manager/model/CssGenerator';

const cssGen = new CssGenerator();

export default PropertyCompositeView.extend({
  templateInput() {
    const pfx = this.pfx;
    const ppfx = this.ppfx;
    return `

        <button type="button" id="${pfx}add" data-add-layer>+</button>

    `;
  },

  init() {
    const model = this.model;
    const pfx = this.pfx;
    model.set('stackIndex', null);
    this.events[`click [data-add-layer]`] = 'addLayer';
    this.listenTo(model, 'change:stackIndex', this.indexChanged);
    this.listenTo(model, 'updateValue', this.inputValueChanged);
    this.delegateEvents();
  },

  /**
   * Fired when the target is updated.
   * With detached mode the component will be always empty as its value
   * so we gonna check all props and find if it has any difference
   * */
  targetUpdated(...args) {
    if (!this.model.get('detached')) {
      PropertyCompositeView.prototype.targetUpdated.apply(this, args);
    } else {
      const { status } = this._getTargetData();
      this.setStatus(status);
      this.checkVisibility();
    }

    this.refreshLayers();
  },

  /**
   * Returns the collection of layers
   * @return {Collection}
   */
  getLayers() {
    return this.model.get('layers');
  },

  /**
   * Triggered when another layer has been selected.
   * This allow to move all rendered properties to a new
   * selected layer
   * @param {Event}
   *
   * @return {Object}
   * */
  indexChanged(e) {
    const model = this.model;
    this.getLayers().active(model.get('stackIndex'));
  },

  addLayer() {
    const model = this.model;
    const layers = this.getLayers();
    const properties = model.get('properties').deepClone();
    properties.each(property => property.set('value', ''));
    const layer = layers.add({ properties });

    // In detached mode inputValueChanged will add new 'layer value'
    // to all subprops
    this.inputValueChanged();

    // This will set subprops with a new default values
    model.set('stackIndex', layers.indexOf(layer));
  },

  inputValueChanged() {
    const model = this.model;
    this.elementUpdated();

    // If not detached I'll just put all the values from layers to property
    // eg. background: layer1Value, layer2Value, layer3Value, ...
    if (!model.get('detached')) {
      model.set('value', this.getLayerValues());
    } else {
      model.get('properties').each(prop => prop.trigger('change:value'));
    }
  },

  /**
   * There is no need to handle input update by the property itself,
   * this will be done by layers
   * @private
   */
  setValue() {},

  /**
   * Create value by layers
   * @return string
   * */
  getLayerValues() {
    return this.getLayers().getFullValue();
  },

  _getClassRule(opts = {}) {
    const { em } = this;
    const { skipAdd = 1 } = opts;
    const selected = em.getSelected();
    const targetAlt = em.get('StyleManager').getModelToStyle(selected, {
      skipAdd,
      useClasses: 1
    });
    return targetAlt !== selected && targetAlt;
  },

  /**
   * Return the parent style rule of the passed one
   * @private
   */
  _getParentTarget(target, opts = {}) {
    const { em, model } = this;
    const property = model.get('property');
    const isValid = opts.isValid || (rule => rule.getStyle()[property]);
    const targetsDevice = em
      .get('CssComposer')
      .getAll()
      .filter(rule => rule.selectorsToString() === target.getSelectorsString());
    const map = targetsDevice.reduce((acc, rule) => {
      acc[rule.getAtRule()] = rule;
      return acc;
    }, {});
    const mapSorted = cssGen.sortMediaObject(map);
    const sortedRules = mapSorted.map(item => item.value);
    const currIndex = sortedRules.indexOf(target);
    const rulesToCheck = sortedRules.splice(0, currIndex);
    let result;

    for (let i = rulesToCheck.length - 1; i > -1; i--) {
      const rule = rulesToCheck[i];
      if (isValid(rule)) {
        // only for not detached
        result = rule;
        break;
      }
    }

    return result;
  },

  /**
   * Refresh layers
   * */
  refreshLayers() {
    let layersObj = [];
    const { model, em } = this;
    const layers = this.getLayers();
    const detached = model.get('detached');
    const property = model.get('property');
    const target = this.getTarget();
    const valueComput = this.getComputedValue();
    const selected = em.getSelected();
    let style, targetAlt, targetAltDevice;

    // With detached layers values will be assigned to their properties
    if (detached) {
      style = target ? target.getStyle() : {};

      // If the style object is empty but the target has a computed value,
      // that means the style might exist in some other place
      if (!keys(style).length && valueComput && selected) {
        // Styles of the same target but with a higher rule
        const nameFirstProp = model
          .get('properties')
          .at(0)
          .get('property');
        targetAltDevice = this._getParentTarget(target, {
          isValid: rule => !isUndefined(rule.getStyle()[nameFirstProp])
        });

        if (targetAltDevice) {
          style = targetAltDevice.getStyle();
        } else {
          // The target is a component but the style is in the class rules
          targetAlt = this._getClassRule();
          style = targetAlt ? targetAlt.getStyle() : {};
        }
      }

      layersObj = layers.getLayersFromStyle(style);
    } else {
      const valueTrg = this.getTargetValue({ ignoreDefault: 1 });
      let value = valueTrg;

      // Try to check if the style is in another rule
      if (!value && valueComput) {
        // Styles of the same target but with a higher rule
        targetAltDevice = this._getParentTarget(target);

        if (targetAltDevice) {
          value = targetAltDevice.getStyle()[property];
        } else {
          // Computed value is not always reliable due to the browser's CSSOM parser
          // here we try to look for the style in class rules
          targetAlt = this._getClassRule();
          const valueTargetAlt = targetAlt && targetAlt.getStyle()[property];
          targetAltDevice =
            !valueTargetAlt &&
            this._getParentTarget(this._getClassRule({ skipAdd: 0 }));
          const valueTrgAltDvc =
            targetAltDevice && targetAltDevice.getStyle()[property];
          value = valueTargetAlt || valueTrgAltDvc || valueComput;
        }
      }

      value = value == model.getDefaultValue() ? '' : value;
      layersObj = layers.getLayersFromValue(value);
    }

    const toAdd = model.getLayersFromTarget(target) || layersObj;
    layers.reset();
    layers.add(toAdd);
    model.set({ stackIndex: null }, { silent: true });
  },

  getTargetValue(opts = {}) {
    let result = PropertyCompositeView.prototype.getTargetValue.call(
      this,
      opts
    );
    const { detached } = this.model.attributes;

    // It might happen that the browser split properties on CSSOM parse
    if (isUndefined(result) && !detached) {
      result = this.model.getValueFromStyle(this.getTarget().getStyle());
    }

    return result;
  },

  onRender() {
    const self = this;
    const model = this.model;
    const fieldEl = this.el.querySelector('[data-layers-wrapper]');
    const PropertiesView = require('./PropertiesView').default;
    const propsConfig = {
      target: this.target,
      propTarget: this.propTarget,

      // Things to do when a single sub-property is changed
      onChange(el, view, opt) {
        const subModel = view.model;

        if (model.get('detached')) {
          const subProp = subModel.get('property');
          const values = self.getLayers().getPropertyValues(subProp);
          view.updateTargetStyle(values, null, opt);
        } else {
          // Update only if there is an actual update (to avoid changes for computed styles)
          // ps: status is calculated in `targetUpdated` method
          if (model.get('status') == 'updated') {
            const value = model.getFullValue();
            model.set('value', value, opt);
            // Try to remove detached properties
            !value && view.updateTargetStyle(value, null, opt);
          }
        }
      }
    };
    const layers = new LayersView({
      collection: this.getLayers(),
      stackModel: model,
      preview: model.get('preview'),
      config: this.config,
      propsConfig
    }).render().el;

    // Will use it to propogate changes
    new PropertiesView({
      target: this.target,
      collection: this.model.get('properties'),
      stackModel: model,
      config: this.config,
      onChange: propsConfig.onChange,
      propTarget: propsConfig.propTarget
    }).render();

    //model.get('properties')
    fieldEl.appendChild(layers);
  }
});
<|endoftext|>


<commit_before>
import React from 'react'
import ReactDOM from 'react-dom'
import {Router, Route, IndexRoute, hashHistory} from 'react-router'
import App from './components/app'
import ButtonRoute from './components/buttonRoute'
import CardRoute from './components/cardRoute'
import CheckboxRoute from './components/checkboxRoute'
import ChipRoute from './components/chipRoute'
import HomeRoute from './components/homeRoute'
import ListRoute from './components/listRoute'
import MenuRoute from './components/menuRoute'
import ModalRoute from './components/modalRoute'
import ProgressRoute from './components/progressRoute'
import RadiobuttonRoute from './components/radiobuttonRoute'
import SelectRoute from './components/selectRoute'
import SliderRoute from './components/sliderRoute'
import SnackbarRoute from './components/snackbarRoute'
import SparklineRoute from './components/sparklineRoute'
import SwitchRoute from './components/switchRoute'
import TabsRoute from './components/tabsRoute'
import TextfieldRoute from './components/textfieldRoute'
import TooltipRoute from './components/tooltipRoute'

// required for tabs component demo
class Germany extends React.Component {
  render () {
    return (

        Germany

    )
  }
}

class Spain extends React.Component {
  render () {
    return (

        Spain

    )
  }
}

class Sweden extends React.Component {
  render () {
    return (

        Sweden

    )
  }
}

ReactDOM.render((
  <Router history={hashHistory}>
    <Route path='/' component={App}>
      <IndexRoute component={HomeRoute} />
      <Route path='button' component={ButtonRoute} />
      <Route path='card' component={CardRoute} />
      <Route path='checkbox' component={CheckboxRoute} />
      <Route path='chip' component={ChipRoute} />
      <Route path='list' component={ListRoute} />
      <Route path='menu' component={MenuRoute} />
      <Route path='modal' component={ModalRoute} />
      <Route path='progress' component={ProgressRoute} />
      <Route path='radiobutton' component={RadiobuttonRoute} />
      <Route path='select' component={SelectRoute} />
      <Route path='slider' component={SliderRoute} />
      <Route path='snackbar' component={SnackbarRoute} />
      <Route path='sparkline' component={SparklineRoute} />
      <Route path='switch' component={SwitchRoute} />
      <Route path='/tabs' component={TabsRoute}>
        <Route path='germany' component={Germany} />
        <Route path='spain' component={Spain} />
        <Route path='sweden' component={Sweden} />
      </Route>
      <Route path='textfield' component={TextfieldRoute} />
      <Route path='tooltip' component={TooltipRoute} />
    </Route>
  </Router>
), document.getElementById('react'))
<commit_msg>revert examples main script<commit_after>
import React from 'react'
import ReactDOM from 'react-dom'
import {Router, Route, IndexRoute, hashHistory} from 'react-router'
import App from './components/app'
import ButtonRoute from './components/buttonRoute'
import CardRoute from './components/cardRoute'
import CheckboxRoute from './components/checkboxRoute'
import ChipRoute from './components/chipRoute'
import HomeRoute from './components/homeRoute'
import IconRoute from './components/iconRoute'
import ListRoute from './components/listRoute'
import MenuRoute from './components/menuRoute'
import ModalRoute from './components/modalRoute'
import ProgressRoute from './components/progressRoute'
import RadiobuttonRoute from './components/radiobuttonRoute'
import SelectRoute from './components/selectRoute'
import SliderRoute from './components/sliderRoute'
import SnackbarRoute from './components/snackbarRoute'
import SparklineRoute from './components/sparklineRoute'
import SwitchRoute from './components/switchRoute'
import TabsRoute from './components/tabsRoute'
import TextfieldRoute from './components/textfieldRoute'
import TooltipRoute from './components/tooltipRoute'

// required for tabs component demo
class Germany extends React.Component {
  render () {
    return (

        Germany

    )
  }
}

class Spain extends React.Component {
  render () {
    return (

        Spain

    )
  }
}

class Sweden extends React.Component {
  render () {
    return (

        Sweden

    )
  }
}

ReactDOM.render((
  <Router history={hashHistory}>
    <Route path='/' component={App}>
      <IndexRoute component={HomeRoute} />
      <Route path='button' component={ButtonRoute} />
      <Route path='card' component={CardRoute} />
      <Route path='checkbox' component={CheckboxRoute} />
      <Route path='chip' component={ChipRoute} />
      <Route path='icon' component={IconRoute} />
      <Route path='list' component={ListRoute} />
      <Route path='menu' component={MenuRoute} />
      <Route path='modal' component={ModalRoute} />
      <Route path='progress' component={ProgressRoute} />
      <Route path='radiobutton' component={RadiobuttonRoute} />
      <Route path='select' component={SelectRoute} />
      <Route path='slider' component={SliderRoute} />
      <Route path='snackbar' component={SnackbarRoute} />
      <Route path='sparkline' component={SparklineRoute} />
      <Route path='switch' component={SwitchRoute} />
      <Route path='/tabs' component={TabsRoute}>
        <Route path='germany' component={Germany} />
        <Route path='spain' component={Spain} />
        <Route path='sweden' component={Sweden} />
      </Route>
      <Route path='textfield' component={TextfieldRoute} />
      <Route path='tooltip' component={TooltipRoute} />
    </Route>
  </Router>
), document.getElementById('react'))
<|endoftext|>


<commit_before>"use strict";

//Load dependencies
var async = require("async");
var applicationStorage = process.require("core/applicationStorage.js");
var rankModel = process.require("ranks/rankModel.js");
var realmModel = process.require("realms/realmModel.js");
var guildModel = process.require("guilds/guildModel.js");
var guildProgressModel = process.require("guildProgress/guildModel.js");

module.exports.getRank = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    async.parallel({
        world: function (callback) {
            rankModel.getRank(req.params.tier, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        region: function (callback) {
            rankModel.getRank(req.params.tier + "_" + req.params.region, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        realm: function (callback) {
            realmModel.findOne({
                region: req.params.region,
                name: req.params.realm
            }, {connected_realms: 1}, function (error, realm) {
                if (realm) {
                    rankModel.getRank(req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_'), req.params.region, req.params.realm, req.params.name, function (error, rank) {
                        callback(error, rank)
                    });
                } else {
                    logger.warn("Realm %s-%s not found", req.params.region, req.params.realm);
                    callback(error);
                }
            });
        }
    }, function (error, result) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (result.world !== null && result.region != null) {
            result.world++;
            result.region++;
            if (result.realm != null)
                result.realm++;
            res.json(result);
        } else {
            next();
        }
    });
};

module.exports.getRanking = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    var start = 0;
    if (req.query && req.query.start) {
        start = parseInt(req.query.start, 10) > 0 ? parseInt(req.query.start, 10) - 1 : 0
    }

    var end = start + 500;
    if (req.query && req.query.limit) {
        var limit = parseInt(req.query.limit, 10);

        if (limit < 0)
            limit = 500;

        if (limit > 1000)
            limit = 1000;

        end = start + limit;
    }

    async.waterfall([
        function (callback) {
            var key = req.params.tier;
            if (req.params.realm && req.params.region) {
                realmModel.findOne({
                    region: req.params.region,
                    name: req.params.realm
                }, {connected_realms: 1}, function (error, realm) {
                    if (realm) {
                        key = req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_');
                        callback(error, key);
                    } else {
                        callback(new Error("Realm %s-%s not found", req.params.region, req.params.realm));
                    }
                });
            } else if (req.params.region) {
                key = req.params.tier + "_" + req.params.region;
                callback(null, key);
            } else {
                callback(null, key);
            }
        },
        function (key, callback) {
            rankModel.getRanking(key, start, end, function (error, ranking) {
                callback(error, ranking);
            });
        }
    ], function (error, ranking) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (ranking) {
            var finalRanking = {};
            var counter = 1;
            async.forEachSeries(ranking, function (rank, callback) {
                var rankArray = rank.split('-');

                finalRanking[start + counter] = {region: rankArray[0], realm: rankArray[1], name: rankArray[2]};
                async.parallel([
                    function(callback){
                        //GET GUILD SIDE and add it
                        guildModel.getSide(rankArray[0],rankArray[1],rankArray[2],function(error,guild){
                            if(guild && guild.bnet && guild.bnet.side!=null ){
                                finalRanking[start + counter]["side"] = guild.bnet.side;
                            }
                            callback(error);
                        });
                    },
                    function(callback){
                        //GET GUILD Progress and add it
                        var projection = {};
                        projection["progress.tier_"+req.params.tier+".normalCount"] = 1;
                        projection["progress.tier_"+req.params.tier+".heroicCount"] = 1;
                        projection["progress.tier_"+req.params.tier+".mythicCount"] = 1;

                        guildProgressModel.find({
                            region: req.params.region,
                            realm: req.params.realm,
                            name: req.params.name
                        }, projection, function (error, guilds) {

                            if (guilds && guilds.length > 0 && guilds[0]['progress'] && guilds[0]['progress']["tier_"+req.params.tier]) {
                                finalRanking[start + counter]["progress"] = guilds[0];
                            }
                            callback(error);

                        });
                    }
                ],function(error){
                    counter++;
                    callback(error);
                })

            }, function () {
                res.json(finalRanking);
            });
        } else {
            next();
        }
    });

};<commit_msg>Add progress on ranking<commit_after>"use strict";

//Load dependencies
var async = require("async");
var applicationStorage = process.require("core/applicationStorage.js");
var rankModel = process.require("ranks/rankModel.js");
var realmModel = process.require("realms/realmModel.js");
var guildModel = process.require("guilds/guildModel.js");
var guildProgressModel = process.require("guildProgress/guildProgressModel.js");

module.exports.getRank = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    async.parallel({
        world: function (callback) {
            rankModel.getRank(req.params.tier, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        region: function (callback) {
            rankModel.getRank(req.params.tier + "_" + req.params.region, req.params.region, req.params.realm, req.params.name, function (error, rank) {
                callback(error, rank)
            });
        },
        realm: function (callback) {
            realmModel.findOne({
                region: req.params.region,
                name: req.params.realm
            }, {connected_realms: 1}, function (error, realm) {
                if (realm) {
                    rankModel.getRank(req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_'), req.params.region, req.params.realm, req.params.name, function (error, rank) {
                        callback(error, rank)
                    });
                } else {
                    logger.warn("Realm %s-%s not found", req.params.region, req.params.realm);
                    callback(error);
                }
            });
        }
    }, function (error, result) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (result.world !== null && result.region != null) {
            result.world++;
            result.region++;
            if (result.realm != null)
                result.realm++;
            res.json(result);
        } else {
            next();
        }
    });
};

module.exports.getRanking = function (req, res, next) {

    var logger = applicationStorage.logger;
    logger.info("%s %s %s %s", req.headers['x-forwarded-for'] || req.connection.remoteAddress, req.method, req.path, JSON.stringify(req.params));

    var start = 0;
    if (req.query && req.query.start) {
        start = parseInt(req.query.start, 10) > 0 ? parseInt(req.query.start, 10) - 1 : 0
    }

    var end = start + 500;
    if (req.query && req.query.limit) {
        var limit = parseInt(req.query.limit, 10);

        if (limit < 0)
            limit = 500;

        if (limit > 1000)
            limit = 1000;

        end = start + limit;
    }

    async.waterfall([
        function (callback) {
            var key = req.params.tier;
            if (req.params.realm && req.params.region) {
                realmModel.findOne({
                    region: req.params.region,
                    name: req.params.realm
                }, {connected_realms: 1}, function (error, realm) {
                    if (realm) {
                        key = req.params.tier + "_" + req.params.region + "_" + realm.connected_realms.join('_');
                        callback(error, key);
                    } else {
                        callback(new Error("Realm %s-%s not found", req.params.region, req.params.realm));
                    }
                });
            } else if (req.params.region) {
                key = req.params.tier + "_" + req.params.region;
                callback(null, key);
            } else {
                callback(null, key);
            }
        },
        function (key, callback) {
            rankModel.getRanking(key, start, end, function (error, ranking) {
                callback(error, ranking);
            });
        }
    ], function (error, ranking) {
        if (error) {
            logger.error(error.message);
            res.status(500).send(error.message);
        } else if (ranking) {
            var finalRanking = {};
            var counter = 1;
            async.forEachSeries(ranking, function (rank, callback) {
                var rankArray = rank.split('-');

                finalRanking[start + counter] = {region: rankArray[0], realm: rankArray[1], name: rankArray[2]};
                async.parallel([
                    function(callback){
                        //GET GUILD SIDE and add it
                        guildModel.getSide(rankArray[0],rankArray[1],rankArray[2],function(error,guild){
                            if(guild && guild.bnet && guild.bnet.side!=null ){
                                finalRanking[start + counter]["side"] = guild.bnet.side;
                            }
                            callback(error);
                        });
                    },
                    function(callback){
                        //GET GUILD Progress and add it
                        var projection = {};
                        projection["progress.tier_"+req.params.tier+".normalCount"] = 1;
                        projection["progress.tier_"+req.params.tier+".heroicCount"] = 1;
                        projection["progress.tier_"+req.params.tier+".mythicCount"] = 1;

                        guildProgressModel.find({
                            region: req.params.region,
                            realm: req.params.realm,
                            name: req.params.name
                        }, projection, function (error, guilds) {

                            if (guilds && guilds.length > 0 && guilds[0]['progress'] && guilds[0]['progress']["tier_"+req.params.tier]) {
                                finalRanking[start + counter]["progress"] = guilds[0];
                            }
                            callback(error);

                        });
                    }
                ],function(error){
                    counter++;
                    callback(error);
                })

            }, function () {
                res.json(finalRanking);
            });
        } else {
            next();
        }
    });

};<|endoftext|>


<commit_before>import Ember from 'ember';
import ResetScrollMixin from '../../mixins/reset-scroll';
import SetupSubmitControllerMixin from '../../mixins/setup-submit-controller';
import Analytics from '../../mixins/analytics';
import config from 'ember-get-config';
import loadAll from 'ember-osf/utils/load-relationship';
import permissions from 'ember-osf/const/permissions';
import getRedirectUrl from '../../utils/get-redirect-url';

const {PREPRINTS: {providers}} = config;

// Error handling for API
const handlers = new Map([
    // format: ['Message detail', 'page']
    ['Authentication credentials were not provided.', 'page-not-found'], // 401
    ['You do not have permission to perform this action.', 'page-not-found'], // 403
    ['Not found.', 'page-not-found'], // 404
    ['The requested node is no longer available.', 'resource-deleted'] // 410
]);

/**
 * @module ember-preprints
 * @submodule routes
 */

/**
 * Fetches current preprint. Redirects to preprint provider route if necessary.
 * @class Content Route Handler
 */
export default Ember.Route.extend(Analytics, ResetScrollMixin, SetupSubmitControllerMixin, {
    theme: Ember.inject.service(),
    headTagsService: Ember.inject.service('head-tags'),
    currentUser: Ember.inject.service('currentUser'),

    setupController(controller, model) {
        controller.set('activeFile', model.get('primaryFile'));
        controller.set('node', this.get('node'));
        Ember.run.scheduleOnce('afterRender', this, function() {
            MathJax.Hub.Queue(['Typeset', MathJax.Hub, [Ember.$('.abstract')[0], Ember.$('#preprintTitle')[0]]]);  // jshint ignore:line
        });

        return this._super(...arguments);
    },

    afterModel(preprint) {
        const {location: {origin}} = window;
        let contributors = Ember.A();

        return preprint.get('provider')
            .then(provider => {
                const providerId = provider.get('id');
                const themeId = this.get('theme.id');
                const isOSF = providerId === 'osf';

                // If we're on the proper branded site, stay here.
                if ((!themeId && isOSF) || themeId === providerId)
                    return Promise.all([
                        provider,
                        preprint.get('node')
                    ]);

                // Otherwise, find the correct provider and redirect
                const configProvider = providers.find(p => p.id === providerId);

                if (!configProvider)
                    throw new Error('Provider is not configured properly. Check the Ember configuration.');

                const {domain} = configProvider;
                const urlParts = [];

                // Provider with a domain
                if (this.get('theme.isDomain') || domain) {
                    urlParts.push(getRedirectUrl(window.location, domain));
                // Provider without a domain
                } else {
                    urlParts.push(origin);

                    if (!isOSF)
                        urlParts.push('preprints', providerId);

                    urlParts.push(preprint.get('id'));
                }

                const url = urlParts.join('/').replace(/\/\/$/, '/');

                window.location.replace(url);

                return Promise.reject();
            })
            .then(([provider, node]) => {
                this.set('node', node);

                if (this.get('editMode')) {
                    const userPermissions = this.get('node.currentUserPermissions') || [];

                    if (!userPermissions.includes(permissions.ADMIN)) {
                        this.replaceWith('forbidden'); // Non-admin trying to access edit form.
                    }
                }

                return Promise.all([
                    provider,
                    node,
                    preprint.get('license'),
                    preprint.get('primaryFile'),
                    loadAll(node, 'contributors', contributors)
                ]);
            })
            .then(([provider, node, license, primaryFile]) => {
                const title = node.get('title');
                const description = node.get('description');
                const doi = preprint.get('doi');
                const image = this.get('theme.logoSharing');
                const imageUrl = `${origin.replace(/^https/, 'http')}${image.path}`;
                const dateCreated = new Date(preprint.get('dateCreated') || null);
                const dateModified = new Date(preprint.get('dateModified') || dateCreated);
                if (!preprint.get('datePublished'))
                    preprint.set('datePublished', dateCreated);
                const providerName = provider.get('name');
                const canonicalUrl = preprint.get('links.html');

                // NOTE: Ordering of meta tags matters for scrapers (Facebook, LinkedIn, Google, etc)

                // Open Graph Protocol
                const openGraph = [
                    ['fb:app_id', config.FB_APP_ID],
                    ['og:title', title],
                    ['og:image', imageUrl],
                    ['og:image:secure_url', `${origin}${image.path}`], // We should always be on https in staging/prod
                    ['og:image:width', image.width.toString()],
                    ['og:image:height', image.height.toString()],
                    ['og:image:type', image.type],
                    ['og:url', canonicalUrl],
                    ['og:description', description],
                    ['og:site_name', providerName],
                    ['og:type', 'article'],
                    ['article:published_time', dateCreated.toISOString()],
                    ['article:modified_time', dateModified.toISOString()]
                ];

                // Highwire Press
                const highwirePress = [
                    ['citation_title', title],
                    ['citation_description', description],
                    ['citation_public_url', canonicalUrl],
                    ['citation_publication_date', `${dateCreated.getFullYear()}/${dateCreated.getMonth() + 1}/${dateCreated.getDate()}`],
                    ['citation_doi', doi]
                ];

                // TODO map Eprints fields
                // Eprints
                const eprints = [];

                // TODO map BE Press fields
                // BE Press
                const bePress = [];

                // TODO map PRISM fields
                // PRISM
                const prism = [];

                // Dublin Core
                const dublinCore = [
                    ['dc.title', title],
                    ['dc.abstract', description],
                    ['dc.identifier', canonicalUrl],
                    ['dc.identifier', doi]
                ];

                const tags = [
                    ...preprint.get('subjects').map(subjectBlock => subjectBlock.map(subject => subject.text)),
                    ...node.get('tags')
                ];

                for (const tag of tags) {
                    openGraph.push(['article:tag', tag]);
                    highwirePress.push(['citation_keywords', tag]);
                    dublinCore.push(['dc.subject', tag]);
                }

                for (const contributor of contributors) {
                    const givenName = contributor.get('users.givenName');
                    const familyName = contributor.get('users.familyName');
                    const fullName = contributor.get('users.fullName');

                    openGraph.push(
                        ['og:type', 'article:author'],
                        ['profile:first_name', givenName],
                        ['profile:last_name', familyName]
                    );
                    highwirePress.push(['citation_author', fullName]);
                    dublinCore.push(['dc.creator', fullName]);
                }

                highwirePress.push(['citation_publisher', providerName]);
                dublinCore.push(
                    ['dc.publisher', providerName],
                    ['dc.license', license ? license.get('name') : 'No license']
                );

                if (/\.pdf$/.test(primaryFile.get('name'))) {
                    highwirePress.push(['citation_pdf_url', primaryFile.get('links').download]);
                }

                const openGraphTags = openGraph
                    .map(([property, content]) => ({
                        property,
                        content
                    }));

                const googleScholarTags = [
                    highwirePress,
                    eprints,
                    bePress,
                    prism,
                    dublinCore
                ]
                    .reduce((a, b) => a.concat(b), [])
                    .map(([name, content]) => ({
                        name,
                        content
                    }));

                const headTags = [
                    ...openGraphTags,
                    ...googleScholarTags
                ]
                    .filter(({content}) => content) // Only show tags with content
                    .map(attrs => ({
                        type: 'meta',
                        attrs
                    }));

                this.set('headTags', headTags);
                this.get('headTagsService').collectHeadTags();
            });
    },

    actions: {
        error(error) {
            // Handle API Errors
            if (error && error.errors && Ember.isArray(error.errors)) {
                const {detail} = error.errors[0];
                const page = handlers.get(detail) || 'page-not-found';

                return this.intermediateTransitionTo(page);
            }
        }
    }
});
<commit_msg>Use MFR url in citation_pdf_url meta tag for Google Scholar<commit_after>import Ember from 'ember';
import ResetScrollMixin from '../../mixins/reset-scroll';
import SetupSubmitControllerMixin from '../../mixins/setup-submit-controller';
import Analytics from '../../mixins/analytics';
import config from 'ember-get-config';
import loadAll from 'ember-osf/utils/load-relationship';
import permissions from 'ember-osf/const/permissions';
import getRedirectUrl from '../../utils/get-redirect-url';

const {
    PREPRINTS: {providers},
    OSF: {renderUrl}
} = config;

// Error handling for API
const handlers = new Map([
    // format: ['Message detail', 'page']
    ['Authentication credentials were not provided.', 'page-not-found'], // 401
    ['You do not have permission to perform this action.', 'page-not-found'], // 403
    ['Not found.', 'page-not-found'], // 404
    ['The requested node is no longer available.', 'resource-deleted'] // 410
]);

/**
 * @module ember-preprints
 * @submodule routes
 */

/**
 * Fetches current preprint. Redirects to preprint provider route if necessary.
 * @class Content Route Handler
 */
export default Ember.Route.extend(Analytics, ResetScrollMixin, SetupSubmitControllerMixin, {
    theme: Ember.inject.service(),
    headTagsService: Ember.inject.service('head-tags'),
    currentUser: Ember.inject.service('currentUser'),

    setupController(controller, model) {
        controller.set('activeFile', model.get('primaryFile'));
        controller.set('node', this.get('node'));
        Ember.run.scheduleOnce('afterRender', this, function() {
            MathJax.Hub.Queue(['Typeset', MathJax.Hub, [Ember.$('.abstract')[0], Ember.$('#preprintTitle')[0]]]);  // jshint ignore:line
        });

        return this._super(...arguments);
    },

    afterModel(preprint) {
        const {location: {origin}} = window;
        let contributors = Ember.A();

        return preprint.get('provider')
            .then(provider => {
                const providerId = provider.get('id');
                const themeId = this.get('theme.id');
                const isOSF = providerId === 'osf';

                // If we're on the proper branded site, stay here.
                if ((!themeId && isOSF) || themeId === providerId)
                    return Promise.all([
                        provider,
                        preprint.get('node')
                    ]);

                // Otherwise, find the correct provider and redirect
                const configProvider = providers.find(p => p.id === providerId);

                if (!configProvider)
                    throw new Error('Provider is not configured properly. Check the Ember configuration.');

                const {domain} = configProvider;
                const urlParts = [];

                // Provider with a domain
                if (this.get('theme.isDomain') || domain) {
                    urlParts.push(getRedirectUrl(window.location, domain));
                // Provider without a domain
                } else {
                    urlParts.push(origin);

                    if (!isOSF)
                        urlParts.push('preprints', providerId);

                    urlParts.push(preprint.get('id'));
                }

                const url = urlParts.join('/').replace(/\/\/$/, '/');

                window.location.replace(url);

                return Promise.reject();
            })
            .then(([provider, node]) => {
                this.set('node', node);

                if (this.get('editMode')) {
                    const userPermissions = this.get('node.currentUserPermissions') || [];

                    if (!userPermissions.includes(permissions.ADMIN)) {
                        this.replaceWith('forbidden'); // Non-admin trying to access edit form.
                    }
                }

                return Promise.all([
                    provider,
                    node,
                    preprint.get('license'),
                    preprint.get('primaryFile'),
                    loadAll(node, 'contributors', contributors)
                ]);
            })
            .then(([provider, node, license, primaryFile]) => {
                const title = node.get('title');
                const description = node.get('description');
                const doi = preprint.get('doi');
                const image = this.get('theme.logoSharing');
                const imageUrl = `${origin.replace(/^https/, 'http')}${image.path}`;
                const dateCreated = new Date(preprint.get('dateCreated') || null);
                const dateModified = new Date(preprint.get('dateModified') || dateCreated);
                if (!preprint.get('datePublished'))
                    preprint.set('datePublished', dateCreated);
                const providerName = provider.get('name');
                const canonicalUrl = preprint.get('links.html');

                // NOTE: Ordering of meta tags matters for scrapers (Facebook, LinkedIn, Google, etc)

                // Open Graph Protocol
                const openGraph = [
                    ['fb:app_id', config.FB_APP_ID],
                    ['og:title', title],
                    ['og:image', imageUrl],
                    ['og:image:secure_url', `${origin}${image.path}`], // We should always be on https in staging/prod
                    ['og:image:width', image.width.toString()],
                    ['og:image:height', image.height.toString()],
                    ['og:image:type', image.type],
                    ['og:url', canonicalUrl],
                    ['og:description', description],
                    ['og:site_name', providerName],
                    ['og:type', 'article'],
                    ['article:published_time', dateCreated.toISOString()],
                    ['article:modified_time', dateModified.toISOString()]
                ];

                // Highwire Press
                const highwirePress = [
                    ['citation_title', title],
                    ['citation_description', description],
                    ['citation_public_url', canonicalUrl],
                    ['citation_publication_date', `${dateCreated.getFullYear()}/${dateCreated.getMonth() + 1}/${dateCreated.getDate()}`],
                    ['citation_doi', doi]
                ];

                // TODO map Eprints fields
                // Eprints
                const eprints = [];

                // TODO map BE Press fields
                // BE Press
                const bePress = [];

                // TODO map PRISM fields
                // PRISM
                const prism = [];

                // Dublin Core
                const dublinCore = [
                    ['dc.title', title],
                    ['dc.abstract', description],
                    ['dc.identifier', canonicalUrl],
                    ['dc.identifier', doi]
                ];

                const tags = [
                    ...preprint.get('subjects').map(subjectBlock => subjectBlock.map(subject => subject.text)),
                    ...node.get('tags')
                ];

                for (const tag of tags) {
                    openGraph.push(['article:tag', tag]);
                    highwirePress.push(['citation_keywords', tag]);
                    dublinCore.push(['dc.subject', tag]);
                }

                for (const contributor of contributors) {
                    const givenName = contributor.get('users.givenName');
                    const familyName = contributor.get('users.familyName');
                    const fullName = contributor.get('users.fullName');

                    openGraph.push(
                        ['og:type', 'article:author'],
                        ['profile:first_name', givenName],
                        ['profile:last_name', familyName]
                    );
                    highwirePress.push(['citation_author', fullName]);
                    dublinCore.push(['dc.creator', fullName]);
                }

                highwirePress.push(['citation_publisher', providerName]);
                dublinCore.push(
                    ['dc.publisher', providerName],
                    ['dc.license', license ? license.get('name') : 'No license']
                );

                highwirePress.push([
                    'citation_pdf_url',
                    `${renderUrl}?url=${encodeURIComponent(`${primaryFile.get('links').download}?direct&mode=render`)}`
                ]);

                const openGraphTags = openGraph
                    .map(([property, content]) => ({
                        property,
                        content
                    }));

                const googleScholarTags = [
                    highwirePress,
                    eprints,
                    bePress,
                    prism,
                    dublinCore
                ]
                    .reduce((a, b) => a.concat(b), [])
                    .map(([name, content]) => ({
                        name,
                        content
                    }));

                const headTags = [
                    ...openGraphTags,
                    ...googleScholarTags
                ]
                    .filter(({content}) => content) // Only show tags with content
                    .map(attrs => ({
                        type: 'meta',
                        attrs
                    }));

                this.set('headTags', headTags);
                this.get('headTagsService').collectHeadTags();
            });
    },

    actions: {
        error(error) {
            // Handle API Errors
            if (error && error.errors && Ember.isArray(error.errors)) {
                const {detail} = error.errors[0];
                const page = handlers.get(detail) || 'page-not-found';

                return this.intermediateTransitionTo(page);
            }
        }
    }
});
<|endoftext|>


<commit_before>
import { format } from "d3-format";
import { timeFormat } from "d3-time-format";

import React from "react";
import PropTypes from "prop-types";

import { ChartCanvas, Chart } from "react-stockcharts";
import {
	BarSeries,
	AreaSeries,
	CandlestickSeries,
	LineSeries,
	MACDSeries,
} from "react-stockcharts/lib/series";
import { XAxis, YAxis } from "react-stockcharts/lib/axes";
import {
	CrossHairCursor,
	EdgeIndicator,
	CurrentCoordinate,
	MouseCoordinateX,
	MouseCoordinateY,
} from "react-stockcharts/lib/coordinates";

import { discontinuousTimeScaleProviderBuilder } from "react-stockcharts/lib/scale";
import { OHLCTooltip, MovingAverageTooltip, MACDTooltip } from "react-stockcharts/lib/tooltip";
import { ema, sma, macd } from "react-stockcharts/lib/indicator";
import { fitWidth } from "react-stockcharts/lib/helper";

function getMaxUndefined(calculators) {
	return calculators.map(each => each.undefinedLength()).reduce((a, b) => Math.max(a, b));
}
const LENGTH_TO_SHOW = 180;

const macdAppearance = {
	stroke: {
		macd: "#FF0000",
		signal: "#00F300",
	},
	fill: {
		divergence: "#4682B4"
	},
};

class CandleStickChartPanToLoadMore extends React.Component {
	constructor(props) {
		super(props);
		const { data: inputData } = props;

		const ema26 = ema()
			.id(0)
			.options({ windowSize: 26 })
			.merge((d, c) => {d.ema26 = c;})
			.accessor(d => d.ema26);

		const ema12 = ema()
			.id(1)
			.options({ windowSize: 12 })
			.merge((d, c) => {d.ema12 = c;})
			.accessor(d => d.ema12);

		const macdCalculator = macd()
			.options({
				fast: 12,
				slow: 26,
				signal: 9,
			})
			.merge((d, c) => {d.macd = c;})
			.accessor(d => d.macd);

		const smaVolume50 = sma()
			.id(3)
			.options({
				windowSize: 50,
				sourcePath: "volume",
			})
			.merge((d, c) => {d.smaVolume50 = c;})
			.accessor(d => d.smaVolume50);

		const maxWindowSize = getMaxUndefined([ema26,
			ema12,
			macdCalculator,
			smaVolume50
		]);
		/* SERVER - START */
		const dataToCalculate = inputData.slice(-LENGTH_TO_SHOW - maxWindowSize);

		const calculatedData = ema26(ema12(macdCalculator(smaVolume50(dataToCalculate))));
		const indexCalculator = discontinuousTimeScaleProviderBuilder().indexCalculator();

		// console.log(inputData.length, dataToCalculate.length, maxWindowSize)
		const { index } = indexCalculator(calculatedData);
		/* SERVER - END */

		const xScaleProvider = discontinuousTimeScaleProviderBuilder()
			.withIndex(index);
		const { data: linearData, xScale, xAccessor, displayXAccessor } = xScaleProvider(calculatedData.slice(-LENGTH_TO_SHOW));

		// console.log(head(linearData), last(linearData))
		// console.log(linearData.length)

		this.state = {
			ema26,
			ema12,
			macdCalculator,
			smaVolume50,
			linearData,
			data: linearData,
			xScale,
			xAccessor, displayXAccessor
		};
		this.handleDownloadMore = this.handleDownloadMore.bind(this);
	}
	handleDownloadMore(start, end) {
		if (Math.ceil(start) === end) return;
		// console.log("rows to download", rowsToDownload, start, end)
		const { data: prevData, ema26, ema12, macdCalculator, smaVolume50 } = this.state;
		const { data: inputData } = this.props;

		if (inputData.length === prevData.length) return;

		const rowsToDownload = end - Math.ceil(start);

		const maxWindowSize = getMaxUndefined([ema26,
			ema12,
			macdCalculator,
			smaVolume50
		]);

		/* SERVER - START */
		const dataToCalculate = inputData
			.slice(-rowsToDownload - maxWindowSize - prevData.length, - prevData.length);

		const calculatedData = ema26(ema12(macdCalculator(smaVolume50(dataToCalculate))));
		const indexCalculator = discontinuousTimeScaleProviderBuilder()
			.initialIndex(Math.ceil(start))
			.indexCalculator();
		const { index, interval } = indexCalculator(
			calculatedData
				.slice(-rowsToDownload)
				.concat(prevData));
		/* SERVER - END */

		const xScaleProvider = discontinuousTimeScaleProviderBuilder()
			.initialIndex(Math.ceil(start))
			.withIndex(index)
			.withInterval(interval);

		const { data: linearData, xScale, xAccessor, displayXAccessor } = xScaleProvider(calculatedData.slice(-rowsToDownload).concat(prevData));

		// console.log(linearData.length)
		setTimeout(() => {
			// simulate a lag for ajax
			this.setState({
				data: linearData,
				xScale,
				xAccessor,
				displayXAccessor,
			});
		}, 300);
	}
	render() {
		const { type, width, ratio } = this.props;
		const { data, ema26, ema12, macdCalculator, smaVolume50, xScale, xAccessor, displayXAccessor } = this.state;

		return (
			<ChartCanvas ratio={ratio} width={width} height={600}
					margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type}
					seriesName="MSFT"
					data={data}
					xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor}
					onLoadMore={this.handleDownloadMore}>
				<Chart id={1} height={400}
						yExtents={[d => [d.high, d.low], ema26.accessor(), ema12.accessor()]}
						padding={{ top: 10, bottom: 20 }}>
					<XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} />
					<YAxis axisAt="right" orient="right" ticks={5} />

					<MouseCoordinateY
						at="right"
						orient="right"
						displayFormat={format(".2f")} />

					<CandlestickSeries />
					<LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/>
					<LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/>

					<CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} />
					<CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} />

					<EdgeIndicator itemType="last" orient="right" edgeAt="right"
						yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/>

					<OHLCTooltip origin={[-40, 0]}/>
					<MovingAverageTooltip
						onClick={(e) => console.log(e)}
						origin={[-38, 15]}
						options={[
							{
								yAccessor: ema26.accessor(),
								type: ema26.type(),
								stroke: ema26.stroke(),
								...ema26.options(),
							},
							{
								yAccessor: ema12.accessor(),
								type: ema12.type(),
								stroke: ema12.stroke(),
								...ema12.options(),
							},
						]}
						/>
				</Chart>
				<Chart id={2} height={150}
						yExtents={[d => d.volume, smaVolume50.accessor()]}
						origin={(w, h) => [0, h - 300]}>
					<YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/>

					<MouseCoordinateY
						at="left"
						orient="left"
						displayFormat={format(".4s")} />

					<BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} />
					<AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/>
				</Chart>
				<Chart id={3} height={150}
						yExtents={macdCalculator.accessor()}
						origin={(w, h) => [0, h - 150]} padding={{ top: 10, bottom: 10 }} >
					<XAxis axisAt="bottom" orient="bottom"/>
					<YAxis axisAt="right" orient="right" ticks={2} />

					<MouseCoordinateX
						at="bottom"
						orient="bottom"
						displayFormat={timeFormat("%Y-%m-%d")} />
					<MouseCoordinateY
						at="right"
						orient="right"
						displayFormat={format(".2f")} />

					<MACDSeries yAccessor={d => d.macd}
						{...macdAppearance} />
					<MACDTooltip
						origin={[-38, 15]}
						yAccessor={d => d.macd}
						options={macdCalculator.options()}
						appearance={macdAppearance}
						/>
				</Chart>
				<CrossHairCursor />
			</ChartCanvas>
		);
	}
}

/*

*/

CandleStickChartPanToLoadMore.propTypes = {
	data: PropTypes.array.isRequired,
	width: PropTypes.number.isRequired,
	ratio: PropTypes.number.isRequired,
	type: PropTypes.oneOf(["svg", "hybrid"]).isRequired,
};

CandleStickChartPanToLoadMore.defaultProps = {
	type: "svg",
};

CandleStickChartPanToLoadMore = fitWidth(CandleStickChartPanToLoadMore);

export default CandleStickChartPanToLoadMore;
<commit_msg>fix error in load on pan<commit_after>
import { format } from "d3-format";
import { timeFormat } from "d3-time-format";

import React from "react";
import PropTypes from "prop-types";

import { ChartCanvas, Chart } from "react-stockcharts";
import {
	BarSeries,
	AreaSeries,
	CandlestickSeries,
	LineSeries,
	MACDSeries,
} from "react-stockcharts/lib/series";
import { XAxis, YAxis } from "react-stockcharts/lib/axes";
import {
	CrossHairCursor,
	EdgeIndicator,
	CurrentCoordinate,
	MouseCoordinateX,
	MouseCoordinateY,
} from "react-stockcharts/lib/coordinates";

import { discontinuousTimeScaleProviderBuilder } from "react-stockcharts/lib/scale";
import { OHLCTooltip, MovingAverageTooltip, MACDTooltip } from "react-stockcharts/lib/tooltip";
import { ema, sma, macd } from "react-stockcharts/lib/indicator";
import { fitWidth } from "react-stockcharts/lib/helper";

function getMaxUndefined(calculators) {
	return calculators.map(each => each.undefinedLength()).reduce((a, b) => Math.max(a, b));
}
const LENGTH_TO_SHOW = 180;

const macdAppearance = {
	stroke: {
		macd: "#FF0000",
		signal: "#00F300",
	},
	fill: {
		divergence: "#4682B4"
	},
};

class CandleStickChartPanToLoadMore extends React.Component {
	constructor(props) {
		super(props);
		const { data: inputData } = props;

		const ema26 = ema()
			.id(0)
			.options({ windowSize: 26 })
			.merge((d, c) => {d.ema26 = c;})
			.accessor(d => d.ema26);

		const ema12 = ema()
			.id(1)
			.options({ windowSize: 12 })
			.merge((d, c) => {d.ema12 = c;})
			.accessor(d => d.ema12);

		const macdCalculator = macd()
			.options({
				fast: 12,
				slow: 26,
				signal: 9,
			})
			.merge((d, c) => {d.macd = c;})
			.accessor(d => d.macd);

		const smaVolume50 = sma()
			.id(3)
			.options({
				windowSize: 50,
				sourcePath: "volume",
			})
			.merge((d, c) => {d.smaVolume50 = c;})
			.accessor(d => d.smaVolume50);

		const maxWindowSize = getMaxUndefined([ema26,
			ema12,
			macdCalculator,
			smaVolume50
		]);
		/* SERVER - START */
		const dataToCalculate = inputData.slice(-LENGTH_TO_SHOW - maxWindowSize);

		const calculatedData = ema26(ema12(macdCalculator(smaVolume50(dataToCalculate))));
		const indexCalculator = discontinuousTimeScaleProviderBuilder().indexCalculator();

		// console.log(inputData.length, dataToCalculate.length, maxWindowSize)
		const { index } = indexCalculator(calculatedData);
		/* SERVER - END */

		const xScaleProvider = discontinuousTimeScaleProviderBuilder()
			.withIndex(index);
		const { data: linearData, xScale, xAccessor, displayXAccessor } = xScaleProvider(calculatedData.slice(-LENGTH_TO_SHOW));

		// console.log(head(linearData), last(linearData))
		// console.log(linearData.length)

		this.state = {
			ema26,
			ema12,
			macdCalculator,
			smaVolume50,
			linearData,
			data: linearData,
			xScale,
			xAccessor, displayXAccessor
		};
		this.handleDownloadMore = this.handleDownloadMore.bind(this);
	}
	handleDownloadMore(start, end) {
		if (Math.ceil(start) === end) return;
		// console.log("rows to download", rowsToDownload, start, end)
		const { data: prevData, ema26, ema12, macdCalculator, smaVolume50 } = this.state;
		const { data: inputData } = this.props;

		if (inputData.length === prevData.length) return;

		const rowsToDownload = end - Math.ceil(start);

		const maxWindowSize = getMaxUndefined([ema26,
			ema12,
			macdCalculator,
			smaVolume50
		]);

		/* SERVER - START */
		const dataToCalculate = inputData
			.slice(-rowsToDownload - maxWindowSize - prevData.length, - prevData.length);

		const calculatedData = ema26(ema12(macdCalculator(smaVolume50(dataToCalculate))));
		const indexCalculator = discontinuousTimeScaleProviderBuilder()
			.initialIndex(Math.ceil(start))
			.indexCalculator();
		const { index } = indexCalculator(
			calculatedData
				.slice(-rowsToDownload)
				.concat(prevData));
		/* SERVER - END */

		const xScaleProvider = discontinuousTimeScaleProviderBuilder()
			.initialIndex(Math.ceil(start))
			.withIndex(index);

		const { data: linearData, xScale, xAccessor, displayXAccessor } = xScaleProvider(calculatedData.slice(-rowsToDownload).concat(prevData));

		// console.log(linearData.length)
		setTimeout(() => {
			// simulate a lag for ajax
			this.setState({
				data: linearData,
				xScale,
				xAccessor,
				displayXAccessor,
			});
		}, 300);
	}
	render() {
		const { type, width, ratio } = this.props;
		const { data, ema26, ema12, macdCalculator, smaVolume50, xScale, xAccessor, displayXAccessor } = this.state;

		return (
			<ChartCanvas ratio={ratio} width={width} height={600}
					margin={{ left: 70, right: 70, top: 20, bottom: 30 }} type={type}
					seriesName="MSFT"
					data={data}
					xScale={xScale} xAccessor={xAccessor} displayXAccessor={displayXAccessor}
					onLoadMore={this.handleDownloadMore}>
				<Chart id={1} height={400}
						yExtents={[d => [d.high, d.low], ema26.accessor(), ema12.accessor()]}
						padding={{ top: 10, bottom: 20 }}>
					<XAxis axisAt="bottom" orient="bottom" showTicks={false} outerTickSize={0} />
					<YAxis axisAt="right" orient="right" ticks={5} />

					<MouseCoordinateY
						at="right"
						orient="right"
						displayFormat={format(".2f")} />

					<CandlestickSeries />
					<LineSeries yAccessor={ema26.accessor()} stroke={ema26.stroke()}/>
					<LineSeries yAccessor={ema12.accessor()} stroke={ema12.stroke()}/>

					<CurrentCoordinate yAccessor={ema26.accessor()} fill={ema26.stroke()} />
					<CurrentCoordinate yAccessor={ema12.accessor()} fill={ema12.stroke()} />

					<EdgeIndicator itemType="last" orient="right" edgeAt="right"
						yAccessor={d => d.close} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"}/>

					<OHLCTooltip origin={[-40, 0]}/>
					<MovingAverageTooltip
						onClick={(e) => console.log(e)}
						origin={[-38, 15]}
						options={[
							{
								yAccessor: ema26.accessor(),
								type: ema26.type(),
								stroke: ema26.stroke(),
								...ema26.options(),
							},
							{
								yAccessor: ema12.accessor(),
								type: ema12.type(),
								stroke: ema12.stroke(),
								...ema12.options(),
							},
						]}
						/>
				</Chart>
				<Chart id={2} height={150}
						yExtents={[d => d.volume, smaVolume50.accessor()]}
						origin={(w, h) => [0, h - 300]}>
					<YAxis axisAt="left" orient="left" ticks={5} tickFormat={format(".2s")}/>

					<MouseCoordinateY
						at="left"
						orient="left"
						displayFormat={format(".4s")} />

					<BarSeries yAccessor={d => d.volume} fill={d => d.close > d.open ? "#6BA583" : "#FF0000"} />
					<AreaSeries yAccessor={smaVolume50.accessor()} stroke={smaVolume50.stroke()} fill={smaVolume50.fill()}/>
				</Chart>
				<Chart id={3} height={150}
						yExtents={macdCalculator.accessor()}
						origin={(w, h) => [0, h - 150]} padding={{ top: 10, bottom: 10 }} >
					<XAxis axisAt="bottom" orient="bottom"/>
					<YAxis axisAt="right" orient="right" ticks={2} />

					<MouseCoordinateX
						at="bottom"
						orient="bottom"
						displayFormat={timeFormat("%Y-%m-%d")} />
					<MouseCoordinateY
						at="right"
						orient="right"
						displayFormat={format(".2f")} />

					<MACDSeries yAccessor={d => d.macd}
						{...macdAppearance} />
					<MACDTooltip
						origin={[-38, 15]}
						yAccessor={d => d.macd}
						options={macdCalculator.options()}
						appearance={macdAppearance}
						/>
				</Chart>
				<CrossHairCursor />
			</ChartCanvas>
		);
	}
}

/*

*/

CandleStickChartPanToLoadMore.propTypes = {
	data: PropTypes.array.isRequired,
	width: PropTypes.number.isRequired,
	ratio: PropTypes.number.isRequired,
	type: PropTypes.oneOf(["svg", "hybrid"]).isRequired,
};

CandleStickChartPanToLoadMore.defaultProps = {
	type: "svg",
};

CandleStickChartPanToLoadMore = fitWidth(CandleStickChartPanToLoadMore);

export default CandleStickChartPanToLoadMore;
<|endoftext|>


<commit_before>'use strict';

// NOTE: ADVANCED_OPTIMIZATIONS mode.
//
// This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
// constructs incompatible with that mode.

/* global $interpolateMinErr: false */
/* global MessageFormatParser: false */
/* global stringify: false */

/**
 * @ngdoc service
 * @name $$messageFormat
 *
 * @description
 * Angular internal service to recognize MessageFormat extensions in interpolation expressions.
 * For more information, see:
 * https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
 *
 * ## Example
 *
 * <example name="ngMessageFormat-example" module="msgFmtExample" deps="angular-message-format.min.js">
 * <file name="index.html">
 *

 *     <button ng-click="decreaseRecipients()" id="decreaseRecipients">decreaseRecipients</button>

 *     {{recipients.length, plural, offset:1
 *             =0    {{{sender.name}} gave no gifts (\#=#)}
 *             =1    {{{sender.name}} gave one gift to {{recipients[0].name}} (\#=#)}
 *             one   {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\#=#)}
 *             other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\#=#)}
 *           }}
 *

 * </file>
 *
 * <file name="script.js">
 *   function Person(name, gender) {
 *     this.name = name;
 *     this.gender = gender;
 *   }
 *
 *   var alice   = new Person("Alice", "female"),
 *       bob     = new Person("Bob", "male"),
 *       charlie = new Person("Charlie", "male"),
 *       harry   = new Person("Harry Potter", "male");
 *
 *   angular.module('msgFmtExample', ['ngMessageFormat'])
 *     .controller('AppController', ['$scope', function($scope) {
 *         $scope.recipients = [alice, bob, charlie];
 *         $scope.sender = harry;
 *         $scope.decreaseRecipients = function() {
 *           --$scope.recipients.length;
 *         };
 *       }]);
 * </file>
 *
 * <file name="protractor.js" type="protractor">
 *   describe('MessageFormat plural', function() {
 *     it('should pluralize initial values', function() {
 *       var messageElem = element(by.binding('recipients.length')), decreaseRecipientsBtn = element(by.id('decreaseRecipients'));
 *       expect(messageElem.getText()).toEqual('Harry Potter gave Alice and 2 other people a gift (#=2)');
 *       decreaseRecipientsBtn.click();
 *       expect(messageElem.getText()).toEqual('Harry Potter gave Alice and one other person a gift (#=1)');
 *       decreaseRecipientsBtn.click();
 *       expect(messageElem.getText()).toEqual('Harry Potter gave one gift to Alice (#=0)');
 *       decreaseRecipientsBtn.click();
 *       expect(messageElem.getText()).toEqual('Harry Potter gave no gifts (#=-1)');
 *     });
 *   });
 * </file>
 * </example>
 */
var $$MessageFormatFactory = ['$parse', '$locale', '$sce', '$exceptionHandler', function $$messageFormat(
                   $parse,   $locale,   $sce,   $exceptionHandler) {

  function getStringifier(trustedContext, allOrNothing, text) {
    return function stringifier(value) {
      try {
        value = trustedContext ? $sce['getTrusted'](trustedContext, value) : $sce['valueOf'](value);
        return allOrNothing && (value === void 0) ? value : stringify(value);
      } catch (err) {
        $exceptionHandler($interpolateMinErr['interr'](text, err));
      }
    };
  }

  function interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
    var stringifier = getStringifier(trustedContext, allOrNothing, text);
    var parser = new MessageFormatParser(text, 0, $parse, $locale['pluralCat'], stringifier,
                                         mustHaveExpression, trustedContext, allOrNothing);
    parser.run(parser.ruleInterpolate);
    return parser.parsedFn;
  }

  return {
    'interpolate': interpolate
  };
}];

var $$interpolateDecorator = ['$$messageFormat', '$delegate', function $$interpolateDecorator($$messageFormat, $interpolate) {
  if ($interpolate['startSymbol']() != "{{" || $interpolate['endSymbol']() != "}}") {
    throw $interpolateMinErr('nochgmustache', 'angular-message-format.js currently does not allow you to use custom start and end symbols for interpolation.');
  }
  var interpolate = $$messageFormat['interpolate'];
  interpolate['startSymbol'] = $interpolate['startSymbol'];
  interpolate['endSymbol'] = $interpolate['endSymbol'];
  return interpolate;
}];

/**
 * @ngdoc module
 * @name ngMessageFormat
 * @packageName angular-message-format
 * @description
 */
var module = window['angular']['module']('ngMessageFormat', ['ng']);
module['factory']('$$messageFormat', $$MessageFormatFactory);
module['config'](['$provide', function($provide) {
  $provide['decorator']('$interpolate', $$interpolateDecorator);
}]);
<commit_msg>docs(ngMessageFormat): add more examples<commit_after>'use strict';

// NOTE: ADVANCED_OPTIMIZATIONS mode.
//
// This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
// constructs incompatible with that mode.

/* global $interpolateMinErr: false */
/* global MessageFormatParser: false */
/* global stringify: false */

/**
 * @ngdoc service
 * @name $$messageFormat
 *
 * @description
 * Angular internal service to recognize MessageFormat extensions in interpolation expressions.
 * For more information, see:
 * https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
 *
 * @example
 * ## Gender
 *
 * This example uses the "select" keyword to specify the message based on gender.
 *
 * <example name="ngMessageFormat-example-gender" module="msgFmtExample" deps="angular-message-format.min.js">
 * <file name="index.html">
 *

 *    Select Recipient:

      <select ng-model="recipient" ng-options="person as person.name for person in recipients">
      </select>

{{recipient.gender, select,
                male {{{recipient.name}} unwrapped his gift. }
                female {{{recipient.name}} unwrapped her gift. }
                other {{{recipient.name}} unwrapped their gift. }
      }}

 *

 * </file>
 * <file name="script.js">
 *   function Person(name, gender) {
 *     this.name = name;
 *     this.gender = gender;
 *   }
 *
 *   var alice   = new Person("Alice", "female"),
 *       bob     = new Person("Bob", "male"),
 *       ashley = new Person("Ashley", "");
 *
 *   angular.module('msgFmtExample', ['ngMessageFormat'])
 *     .controller('AppController', ['$scope', function($scope) {
 *         $scope.recipients = [alice, bob, ashley];
 *         $scope.recipient = $scope.recipients[0];
 *       }]);
 * </file>
 * </example>
 *
 * @example
 * ## Plural
 *
 * This example shows how the "plural" keyword is used to account for a variable number of entities.
 * The "#" variable holds the current number and can be embedded in the message.
 *
 * Note that "=1" takes precedence over "one".
 *
 * The example also shows the "offset" keyword, which allows you to offset the value of the "#" variable.
 *
 * <example name="ngMessageFormat-example-plural" module="msgFmtExample" deps="angular-message-format.min.js">
 * <file name="index.html">
 *

      Select recipients:

      <select multiple size=5 ng-model="recipients" ng-options="person as person.name for person in people">
      </select>

 *
{{recipients.length, plural, offset:1
 *             =0    {{{sender.name}} gave no gifts (\#=#)}
 *             =1    {{{sender.name}} gave a gift to {{recipients[0].name}} (\#=#)}
 *             one   {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\#=#)}
 *             other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\#=#)}
 *           }}

 *

 * </file>
 *
 * <file name="script.js">
 *   function Person(name, gender) {
 *     this.name = name;
 *     this.gender = gender;
 *   }
 *
 *   var alice   = new Person("Alice", "female"),
 *       bob     = new Person("Bob", "male"),
 *       sarah     = new Person("Sarah", "female"),
 *       harry   = new Person("Harry Potter", "male"),
 *       ashley   = new Person("Ashley", "");
 *
 *   angular.module('msgFmtExample', ['ngMessageFormat'])
 *     .controller('AppController', ['$scope', function($scope) {
 *         $scope.people = [alice, bob, sarah, ashley];
 *         $scope.recipients = [alice, bob, sarah];
 *         $scope.sender = harry;
 *       }]);
 * </file>
 *
 * <file name="protractor.js" type="protractor">
 *   describe('MessageFormat plural', function() {
 *     function clickOptionCtrl(select, index) {
 *      element.all(by.css('select option')).then(function(arr) {
           browser.actions()
              .mouseMove(arr[index])
              .keyDown(protractor.Key.CONTROL)
              .click()
              .keyUp(protractor.Key.CONTROL)
              .perform();
        });
 *     }
 *
 *     it('should pluralize initial values', function() {
 *       var messageElem = element(by.binding('recipients.length')),
 *         select = element(by.css('select'));
 *
 *       expect(messageElem.getText()).toEqual('Harry Potter gave Alice and 2 other people a gift (#=2)');
         clickOptionCtrl(select, 2);
 *       expect(messageElem.getText()).toEqual('Harry Potter gave Alice and one other person a gift (#=1)');
         clickOptionCtrl(select, 1);
 *       expect(messageElem.getText()).toEqual('Harry Potter gave a gift to Alice (#=0)');
         clickOptionCtrl(select, 0);
 *       expect(messageElem.getText()).toEqual('Harry Potter gave no gifts (#=-1)');
 *     });
 *   });
 * </file>
 * </example>
 *
 * @example
 * ## Plural and Gender
 *
 * This example shows how you can specify gender rules for specific plural matches - in this case,
 * =1 is special cased for gender.
 * <example name="ngMessageFormat-example-plural-gender" module="msgFmtExample" deps="angular-message-format.min.js">
 *   <file name="index.html">
 *

       Select recipients:

       <select multiple size=5 ng-model="recipients" ng-options="person as person.name for person in people">
       </select>

{{recipients.length, plural,
          =0 {{{sender.name}} has not given any gifts to anyone.}
          =1 {  {{recipients[0].gender, select,
                 female { {{sender.name}} gave {{recipients[0].name}} her gift.}
                 male { {{sender.name}} gave {{recipients[0].name}} his gift.}
                 other { {{sender.name}} gave {{recipients[0].name}} their gift.}
                }}
              }
          other {{{sender.name}} gave {{recipients.length}} people gifts.}
          }}

      </file>
 *    <file name="script.js">
 *      function Person(name, gender) {
 *        this.name = name;
 *        this.gender = gender;
 *      }
 *
 *      var alice   = new Person("Alice", "female"),
 *          bob     = new Person("Bob", "male"),
 *          harry   = new Person("Harry Potter", "male"),
 *          ashley   = new Person("Ashley", "");
 *
 *      angular.module('msgFmtExample', ['ngMessageFormat'])
 *        .controller('AppController', ['$scope', function($scope) {
 *            $scope.people = [alice, bob, ashley];
 *            $scope.recipients = [alice];
 *            $scope.sender = harry;
 *          }]);
 *    </file>
    </example>
 */
var $$MessageFormatFactory = ['$parse', '$locale', '$sce', '$exceptionHandler', function $$messageFormat(
                   $parse,   $locale,   $sce,   $exceptionHandler) {

  function getStringifier(trustedContext, allOrNothing, text) {
    return function stringifier(value) {
      try {
        value = trustedContext ? $sce['getTrusted'](trustedContext, value) : $sce['valueOf'](value);
        return allOrNothing && (value === void 0) ? value : stringify(value);
      } catch (err) {
        $exceptionHandler($interpolateMinErr['interr'](text, err));
      }
    };
  }

  function interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
    var stringifier = getStringifier(trustedContext, allOrNothing, text);
    var parser = new MessageFormatParser(text, 0, $parse, $locale['pluralCat'], stringifier,
                                         mustHaveExpression, trustedContext, allOrNothing);
    parser.run(parser.ruleInterpolate);
    return parser.parsedFn;
  }

  return {
    'interpolate': interpolate
  };
}];

var $$interpolateDecorator = ['$$messageFormat', '$delegate', function $$interpolateDecorator($$messageFormat, $interpolate) {
  if ($interpolate['startSymbol']() != "{{" || $interpolate['endSymbol']() != "}}") {
    throw $interpolateMinErr('nochgmustache', 'angular-message-format.js currently does not allow you to use custom start and end symbols for interpolation.');
  }
  var interpolate = $$messageFormat['interpolate'];
  interpolate['startSymbol'] = $interpolate['startSymbol'];
  interpolate['endSymbol'] = $interpolate['endSymbol'];
  return interpolate;
}];

/**
 * @ngdoc module
 * @name ngMessageFormat
 * @packageName angular-message-format
 * @description
 */
var module = window['angular']['module']('ngMessageFormat', ['ng']);
module['factory']('$$messageFormat', $$MessageFormatFactory);
module['config'](['$provide', function($provide) {
  $provide['decorator']('$interpolate', $$interpolateDecorator);
}]);
<|endoftext|>