<commit_before>/*
 * PageExtractor - An interactive page splitter
 *
 * Copyright(c) 2011 Olivier Favre <of.olivier.favre@gmail.com>
 * See LICENSE file.
 */

if (!window.PageExtractor) window.PageExtractor = {};
if (!window.PageExtractor.Algo) window.PageExtractor.Algo = { super: PageExtractor, root: window.PageExtractor };

window.PageExtractor.Algo.positives = [];
window.PageExtractor.Algo.negatives = [];
window.PageExtractor.Algo.results = [];

window.PageExtractor.Algo.learn = function () {
    //var tmprslt = testRule('/html/body//'+this.positives[0].data[this.positives[0].data.length-1].tag+'[contains(concat(" ",@class," "),"'+this.positives[0].data[this.positives[0].data.length-1].classes[0]+'")]');
    var tmprslt = this.Stats.statElements(this.XPath.getResults('/html/body//'+this.positives[0].data[this.positives[0].data.length-1].tag));
    console.log(tmprslt);
    for (var i = 0 ; i < tmprslt.length ; i++)
        if (tmprslt.stats.similarity.positives_as_ref.by_elmt.max[i] > tmprslt.stats.similarity.negatives_as_ref.by_elmt.max[i])
            this.results.push(tmprslt.elements[i]);

    /*
     * Try using classes and hierarchy first.
     *  - Search for anything (like '/html/body//TAG')
     *  - Sort results by decreasing max similarity with positives
     *  - Find good classes/depth:
     *     - Iterate results in order (decreasing)
     *     - Consider hierarchy from leaf to root
     *     - Note unique classes/depth and n-uples/ and /depth_reversed: use as key
     *     - (Thread not already known key as having a last met rank of 0)
     *     - (¿If last key was met in more than X ranks ago, remove?)
     *     - Else update last met rank, and keep
     *     - Add if not already available
     *     - Update element count and means max similarity with positives and negatives, for that key
     *  - Sort by mean max similarity with positives
     *  - (Prefer: higher similarity with positives, lower similarity with negatives, fewer classes)
     *  - (What now?)
     */
    var index = {};
    var data = [];
    var Criteria = function(classes, depth, depth_is_reversed) {
        if (classes instanceof Array) {
            if (classes.length == 0)
                return undefined;
        } else {
            if (classes == undefined || classes == null)
                return undefined;
            classes = [classes];
        }
        this.classes = classes;
        this.depth = depth;
        this.depth_is_reversed = depth_is_reversed ? true : false;
        this.key = function(){
            return this.classes.length+"["+this.classes.join(",")+"]"+(this.depth_is_reversed?"R":"N")+this.depth;
        };
        var idx = index[this.key()];
        if (idx != undefined)
            return data[idx];
        // Continue here with the new object if not already in index
        this.id = data.length;
        index[this.key()] = data.length;
        data.push(this);
        this.remove = function() {
            var key = this.key()
            var idx = index[key];
            if (idx == undefined) return;
            delete index[key];
            data[idx] = undefined; // don't splice, or it'll shift all other indexes!
        };
        this.element_indexes = [];
        this.mean_max_similarity_with_positives = 0.0;
        this.mean_max_similarity_with_negatives = 0.0;
        this.updateWithNewElement = function(rslt, elmt_idx) {
            this.mean_max_similarity_with_positives = (this.mean_max_similarity_with_positives * this.element_indexes.length + rslt.stats.similarity.positives_as_ref.by_elmt.max[elmt_idx]) / (this.element_indexes.length + 1.0);
            this.mean_max_similarity_with_negatives = (this.mean_max_similarity_with_negatives * this.element_indexes.length + rslt.stats.similarity.negatives_as_ref.by_elmt.max[elmt_idx]) / (this.element_indexes.length + 1.0);
            this.element_indexes.push(elmt_idx);
        };
        return this;
    };
    var result_comparator = function(rslt) {
        var tmp = rslt.stats.similarity.positives_as_ref.by_elmt.max;
        return function(i,j) {
            return tmp[i] - tmp[j]; // decreasing
        };
    };
    var sortedIdx = [];
    for (var i = 0 ; i < tmprslt.length ; i++)
        sortedIdx.push(i);
    sortedIdx.sort(result_comparator(tmprslt));
    var combinations = function(values) {
        var presence = [];
        for (var i = 0 ; i < values.length ; i++)
            presence.push(false);
        var rtn = [];
        var i = 0;
        // The following loop skips "all false", which is intentional
        while (i < presence.length) {
            if (presence[i]) {
                presence[i] = false;
                i++;
                continue;
            } else {
                presence[i] = true;
                i = 0;
            }
            var c = [];
            for (var j = 0 ; j < presence.length ; j++)
                if (presence[j])
                    c.push(values[j]);
            rtn.push(c);
        }
        return rtn;
    };
    for (var i = 0 ; i < tmprslt.length ; i++) {
        var rslt = tmprslt.data[sortedIdx[i]];
        for (var d = rslt.data.length-1 ; d >= 0 ; d--) {
            var elmt = rslt.data[d];
            var cls = combinations(elmt.classes);
            for (var j = 0 ; j < cls.length ; j++) {
                var c = new Criteria(cls[j], elmt.depth, false);
                if (c != undefined)
                    c.updateWithNewElement(tmprslt, sortedIdx[i]);
                c = new Criteria(cls[j], elmt.depth_reversed, true);
                if (c != undefined)
                    c.updateWithNewElement(tmprslt, sortedIdx[i]);
            }
        }
    }
    var data_sortedIdx = [];
    for (var i = 0 ; i < data.length ; i++)
        data_sortedIdx.push(i);
    data_sortedIdx.sort(function(i,j){
        var v;
        v = data[j].mean_max_similarity_with_positives - data[i].mean_max_similarity_with_positives; // decreasing
        if (v != 0) return v;
        v = data[i].mean_max_similarity_with_negatives - data[j].mean_max_similarity_with_negatives; // increasing
        if (v != 0) return v;
        if (data[i].depth_is_reversed == true && data[j].depth_is_reversed == false) return 1;
        if (data[i].depth_is_reversed == true)
            v = data[j].depth - data[i].depth; // decreasing (farther up in hierarchy, from leaf)
        else
            v = data[i].depth - data[j].depth; // increasing (farther down in hierarchy, from root)
        if (v != 0) return v;
        v = data[i].classes.length - data[j].classes.length; // increasing
        if (v != 0) return v;
        v = data[i].classes.join(' ').localeCompare(data[j].classes.join(' ')); // increasing
        if (v != 0) return v;
        // Don't compare element_count at this stage, it's pointless as it only depends on the preceding
        return 0;
    });
    var arff = "@relation PageExtractor\n\n"+
        "@attribute depth real\n"+
        "@attribute depth_reversed {1,0}\n"+
        "@attribute element_count real\n"+
        "@attribute mean_max_similarity_with_positives real\n"+
        "@attribute mean_max_similarity_with_negatives real\n"+
        "@attribute classes string\n"+
        "\n@data\n";
    var instances = [];
    var best;
    for (var i = 0 ; i < data_sortedIdx.length ; i++) {
        var d = data[data_sortedIdx[i]];
        if (d.mean_max_similarity_with_positives > d.mean_max_similarity_with_negatives) {
            if (best == undefined) best = i;
            instances.push([
                d.depth,
                d.depth_is_reversed ? '1' : '0',
                d.element_indexes.length,
                d.mean_max_similarity_with_positives,
                d.mean_max_similarity_with_negatives,
                d.classes.join(' & ')
            ].join(','));
        }
    }
    arff += instances.join('\n');
    this.root.Ui.Arff.setDataExport(arff);

    best = data[data_sortedIdx[best]];
    // TODO: Use the rule, check validity, recurse
    // TODO: Create a criteria class, that generates XPath queries

    /*
     * ARFF export of all elements returned by XPath
     *
    var arff = "@relation PageExtractor\n\n"+
        "@attribute tag string\n"+
        "@attribute position real\n"+
        "@attribute position_reversed real\n"+
        "@attribute position_max real\n"+
        "@attribute children_count real\n"+
        "@attribute text_len real\n"+
        "@attribute depth real\n"+
        "@attribute max_positive_similarity real\n"+
        "@attribute max_negative_similarity real\n";
    var classes_map = {};
    var classes_stub = [];
    for (var i = 0 ; i < tmprslt.length ; i++) {
        var cls = tmprslt.data[i].data.slice(-1)[0].classes;
        for (var j = 0 ; j < cls.length ; j++) {
            if (!classes_map[cls[j]]) {
                classes_map[cls[j]] = classes_stub.length;
                classes_stub.push(0);
                arff += "@attribute class_"+cls[j]+" {1,0}\n";
            }
        }
    }
    arff += "\n@data\n";
    var instances = [];
    for (var i = 0 ; i < tmprslt.length ; i++) {
        var obj = tmprslt.data[i].data.slice(-1)[0];
        var data = [];
        data.push(obj.tag);
        data.push(obj.position);
        data.push(obj.position_reversed);
        data.push(obj.position_max);
        data.push(obj.children_count);
        data.push(obj.text_len);
        data.push(tmprslt.data[i].data.length);
        var v;
        v = tmprslt.stats.similarity.positives_as_ref.by_elmt.max[i];
        data.push(v != undefined ? v : 0);
        v = tmprslt.stats.similarity.negatives_as_ref.by_elmt.max[i];
        data.push(v != undefined ? v : 0);
        var cls = classes_stub.slice(0);
        for (var c = 0 ; c < obj.classes.length ; c++)
            cls[classes_map[obj.classes[c]]] = 1;
        data = data.concat(cls);
        instances.push(data.join(","));
    }
    arff += instances.join("\n");
    this.root.Ui.Arff.setDataExport(arff);
    */
}
<commit_msg>Remove old ARFF export code<commit_after>/*
 * PageExtractor - An interactive page splitter
 *
 * Copyright(c) 2011 Olivier Favre <of.olivier.favre@gmail.com>
 * See LICENSE file.
 */

if (!window.PageExtractor) window.PageExtractor = {};
if (!window.PageExtractor.Algo) window.PageExtractor.Algo = { super: PageExtractor, root: window.PageExtractor };

window.PageExtractor.Algo.positives = [];
window.PageExtractor.Algo.negatives = [];
window.PageExtractor.Algo.results = [];

window.PageExtractor.Algo.learn = function () {
    //var tmprslt = testRule('/html/body//'+this.positives[0].data[this.positives[0].data.length-1].tag+'[contains(concat(" ",@class," "),"'+this.positives[0].data[this.positives[0].data.length-1].classes[0]+'")]');
    var tmprslt = this.Stats.statElements(this.XPath.getResults('/html/body//'+this.positives[0].data[this.positives[0].data.length-1].tag));
    console.log(tmprslt);
    for (var i = 0 ; i < tmprslt.length ; i++)
        if (tmprslt.stats.similarity.positives_as_ref.by_elmt.max[i] > tmprslt.stats.similarity.negatives_as_ref.by_elmt.max[i])
            this.results.push(tmprslt.elements[i]);

    /*
     * Try using classes and hierarchy first.
     *  - Search for anything (like '/html/body//TAG')
     *  - Sort results by decreasing max similarity with positives
     *  - Find good classes/depth:
     *     - Iterate results in order (decreasing)
     *     - Consider hierarchy from leaf to root
     *     - Note unique classes/depth and n-uples/ and /depth_reversed: use as key
     *     - (Thread not already known key as having a last met rank of 0)
     *     - (¿If last key was met in more than X ranks ago, remove?)
     *     - Else update last met rank, and keep
     *     - Add if not already available
     *     - Update element count and means max similarity with positives and negatives, for that key
     *  - Sort by mean max similarity with positives
     *  - (Prefer: higher similarity with positives, lower similarity with negatives, fewer classes)
     *  - (What now?)
     */
    var index = {};
    var data = [];
    var Criteria = function(classes, depth, depth_is_reversed) {
        if (classes instanceof Array) {
            if (classes.length == 0)
                return undefined;
        } else {
            if (classes == undefined || classes == null)
                return undefined;
            classes = [classes];
        }
        this.classes = classes;
        this.depth = depth;
        this.depth_is_reversed = depth_is_reversed ? true : false;
        this.key = function(){
            return this.classes.length+"["+this.classes.join(",")+"]"+(this.depth_is_reversed?"R":"N")+this.depth;
        };
        var idx = index[this.key()];
        if (idx != undefined)
            return data[idx];
        // Continue here with the new object if not already in index
        this.id = data.length;
        index[this.key()] = data.length;
        data.push(this);
        this.remove = function() {
            var key = this.key()
            var idx = index[key];
            if (idx == undefined) return;
            delete index[key];
            data[idx] = undefined; // don't splice, or it'll shift all other indexes!
        };
        this.element_indexes = [];
        this.mean_max_similarity_with_positives = 0.0;
        this.mean_max_similarity_with_negatives = 0.0;
        this.updateWithNewElement = function(rslt, elmt_idx) {
            this.mean_max_similarity_with_positives = (this.mean_max_similarity_with_positives * this.element_indexes.length + rslt.stats.similarity.positives_as_ref.by_elmt.max[elmt_idx]) / (this.element_indexes.length + 1.0);
            this.mean_max_similarity_with_negatives = (this.mean_max_similarity_with_negatives * this.element_indexes.length + rslt.stats.similarity.negatives_as_ref.by_elmt.max[elmt_idx]) / (this.element_indexes.length + 1.0);
            this.element_indexes.push(elmt_idx);
        };
        return this;
    };
    var result_comparator = function(rslt) {
        var tmp = rslt.stats.similarity.positives_as_ref.by_elmt.max;
        return function(i,j) {
            return tmp[i] - tmp[j]; // decreasing
        };
    };
    var sortedIdx = [];
    for (var i = 0 ; i < tmprslt.length ; i++)
        sortedIdx.push(i);
    sortedIdx.sort(result_comparator(tmprslt));
    var combinations = function(values) {
        var presence = [];
        for (var i = 0 ; i < values.length ; i++)
            presence.push(false);
        var rtn = [];
        var i = 0;
        // The following loop skips "all false", which is intentional
        while (i < presence.length) {
            if (presence[i]) {
                presence[i] = false;
                i++;
                continue;
            } else {
                presence[i] = true;
                i = 0;
            }
            var c = [];
            for (var j = 0 ; j < presence.length ; j++)
                if (presence[j])
                    c.push(values[j]);
            rtn.push(c);
        }
        return rtn;
    };
    for (var i = 0 ; i < tmprslt.length ; i++) {
        var rslt = tmprslt.data[sortedIdx[i]];
        for (var d = rslt.data.length-1 ; d >= 0 ; d--) {
            var elmt = rslt.data[d];
            var cls = combinations(elmt.classes);
            for (var j = 0 ; j < cls.length ; j++) {
                var c = new Criteria(cls[j], elmt.depth, false);
                if (c != undefined)
                    c.updateWithNewElement(tmprslt, sortedIdx[i]);
                c = new Criteria(cls[j], elmt.depth_reversed, true);
                if (c != undefined)
                    c.updateWithNewElement(tmprslt, sortedIdx[i]);
            }
        }
    }
    var data_sortedIdx = [];
    for (var i = 0 ; i < data.length ; i++)
        data_sortedIdx.push(i);
    data_sortedIdx.sort(function(i,j){
        var v;
        v = data[j].mean_max_similarity_with_positives - data[i].mean_max_similarity_with_positives; // decreasing
        if (v != 0) return v;
        v = data[i].mean_max_similarity_with_negatives - data[j].mean_max_similarity_with_negatives; // increasing
        if (v != 0) return v;
        if (data[i].depth_is_reversed == true && data[j].depth_is_reversed == false) return 1;
        if (data[i].depth_is_reversed == true)
            v = data[j].depth - data[i].depth; // decreasing (farther up in hierarchy, from leaf)
        else
            v = data[i].depth - data[j].depth; // increasing (farther down in hierarchy, from root)
        if (v != 0) return v;
        v = data[i].classes.length - data[j].classes.length; // increasing
        if (v != 0) return v;
        v = data[i].classes.join(' ').localeCompare(data[j].classes.join(' ')); // increasing
        if (v != 0) return v;
        // Don't compare element_count at this stage, it's pointless as it only depends on the preceding
        return 0;
    });
    var arff = "@relation PageExtractor\n\n"+
        "@attribute depth real\n"+
        "@attribute depth_reversed {1,0}\n"+
        "@attribute element_count real\n"+
        "@attribute mean_max_similarity_with_positives real\n"+
        "@attribute mean_max_similarity_with_negatives real\n"+
        "@attribute classes string\n"+
        "\n@data\n";
    var instances = [];
    var best;
    for (var i = 0 ; i < data_sortedIdx.length ; i++) {
        var d = data[data_sortedIdx[i]];
        if (d.mean_max_similarity_with_positives > d.mean_max_similarity_with_negatives) {
            if (best == undefined) best = i;
            instances.push([
                d.depth,
                d.depth_is_reversed ? '1' : '0',
                d.element_indexes.length,
                d.mean_max_similarity_with_positives,
                d.mean_max_similarity_with_negatives,
                d.classes.join(' & ')
            ].join(','));
        }
    }
    arff += instances.join('\n');
    this.root.Ui.Arff.setDataExport(arff);

    best = data[data_sortedIdx[best]];
    // TODO: Use the rule, check validity, recurse
    // TODO: Create a criteria class, that generates XPath queries
}
<|endoftext|>


<commit_before>const OSM_MAP_ATTR = {
  type: 'data',
  name: '© OpenStreetMap',
  url: 'https://osm.org/',
};

const OSM_DATA_ATTR = {
  type: 'data',
  nameKey: 'mapLayers.attr.osmData',
  url: 'https://osm.org/copyright',
};

const FM_ATTR = {
  type: 'map',
  nameKey: 'mapLayers.attr.freemap',
};

const SRTM_ATTR = {
  type: 'data',
  nameKey: 'mapLayers.attr.srtm',
};

const STRAVA_ATTR = {
  type: 'map',
  name: '© Strava',
  url: 'https://www.strava.com/',
};

const NLC_ATTR = {
  type: 'map',
  name: '© NLC Zvolen',
  url: 'http://www.nlcsk.org/',
};

const INFOMAPA_ATTR = {
  type: 'map',
  name: '© infomapa.sk',
  url: 'http://www.infomapa.sk/',
};

export const baseLayers = [
  ...[
    ['A', 'car'],
    ['T', '!icon-hiking'],
    ['C', 'bicycle'],
    ['K', '!icon-skier-skiing'],
  ].map(([type, icon]) => ({
    type,
    icon,
    url: `//{s}.freemap.sk/${type}/{z}/{x}/{y}.{tileFormat}`,
    attribution: [
      FM_ATTR,
      OSM_DATA_ATTR,
      type !== 'A' && SRTM_ATTR,
    ].filter(a => a),
    minZoom: 8,
    maxNativeZoom: 16,
    key: type.toLowerCase(),
  })),
  {
    type: 'S',
    icon: 'plane',
    minZoom: 0,
    maxNativeZoom: 18,
    key: 's',
    attribution: [
      {
        type: 'map',
        name: '© Bing',
      },
      {
        type: 'map',
        name: '© Earthstar Geographics SIO',
      },
      {
        type: 'map',
        name: '© 2017 Microsoft Corporation',
      },
    ],
  },
  {
    type: 'O',
    icon: 'globe',
    url: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    minZoom: 0,
    maxNativeZoom: 19,
    attribution: [
      OSM_MAP_ATTR,
      OSM_DATA_ATTR,
    ],
    key: 'o',
  },
  {
    type: 'M',
    url: 'http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png',
    minZoom: 3,
    maxNativeZoom: 18,
    icon: 'bicycle',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        name: '© Martin Tesař',
        url: 'mailto:smmtb@gmail.com',
      },
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    key: 'm',
  },
  {
    type: 'p',
    url: '//tile.opentopomap.org/{z}/{x}/{y}.png',
    minZoom: 3,
    maxNativeZoom: 17,
    icon: 'tree',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        name: '© OpenTopoMap',
        url: 'https://tile.opentopomap.org/about#verwendung',
      },
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    key: 'p',
  },
  {
    type: 'b',
    url: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
    minZoom: 0,
    maxNativeZoom: 20,
    icon: 'medkit',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        nameKey: 'mapLayers.attr.hot',
        url: 'http://hot.openstreetmap.org',
      },
      OSM_DATA_ATTR,
    ],
    key: 'b',
  },
  {
    type: 'd',
    url: '//tile.memomaps.de/tilegen/{z}/{x}/{y}.png',
    minZoom: 0,
    maxNativeZoom: 18,
    icon: 'bus',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        name: '© MeMoMaps',
        url: 'https://memomaps.de/en/',
      },
      OSM_DATA_ATTR,
    ],
    key: 'd',
  },
  {
    type: 'i',
    icon: 'info',
    url: 'http://{s}.infomapa.sk/0/{z}/{x}/{y}',
    minZoom: 8,
    maxNativeZoom: 18,
    showOnlyInExpertMode: true,
    attribution: [
      INFOMAPA_ATTR,
      OSM_DATA_ATTR,
    ],
    key: 'i',
  },
  {
    type: 'j',
    icon: 'info',
    url: 'http://{s}.infomapa.sk/1/{z}/{x}/{y}',
    minZoom: 8,
    maxNativeZoom: 18,
    showOnlyInExpertMode: true,
    attribution: [
      INFOMAPA_ATTR,
      OSM_DATA_ATTR,
    ],
    key: 'j',
  },
  {
    type: 'h',
    url: 'http://tms.freemap.sk/historicke/{z}/{x}/{y}.png',
    minNativeZoom: 12,
    maxNativeZoom: 12,
    icon: 'institution',
    showOnlyInExpertMode: true,
    attribution: [],
    key: 'h',
  },
];

export const overlayLayers = [
  {
    type: 'I',
    icon: 'picture-o',
    minZoom: 0,
    key: 'F',
    zIndex: 3,
    attribution: [
      {
        type: 'photos',
        name: 'CC-BY-SA',
      },
    ],
  },
  {
    type: 'n',
    icon: 'tree',
    url: '//tiles.freemap.sk/nlc2016/{z}/{x}/{y}.png',
    attribution: [NLC_ATTR],
    minZoom: 11,
    maxNativeZoom: 15,
    key: 'N',
    zIndex: 2,
    adminOnly: true,
  },
  {
    type: 'l',
    icon: 'tree',
    url: 'http://gpsteam.eu/cache/nlcml/{z}/{x}/{y}.png',
    attribution: [NLC_ATTR],
    minZoom: 14,
    maxNativeZoom: 16,
    key: 'L',
    zIndex: 2,
    adminOnly: true,
  },
  ...['both', 'ride', 'run', 'water', 'winter'].map(([type], i) => ({
    type: `s${i}`,
    icon: 'scribd', // TODO use correct logo
    url: `//heatmap-external-{s}.strava.com/tiles/${type}/bluered/{z}/{x}/{y}.png?px=256`,
    attribution: [STRAVA_ATTR],
    minZoom: 0,
    maxNativeZoom: 16,
    key: type === 'both' ? 'H' : undefined,
    showOnlyInExpertMode: true,
    zIndex: 2,
  })),
  {
    type: 'g',
    icon: '!icon-gps-device',
    url: '//gps-{s}.tile.openstreetmap.org/lines/{z}/{x}/{y}.png',
    attribution: [
      OSM_MAP_ATTR,
      OSM_DATA_ATTR,
    ],
    minZoom: 0,
    maxNativeZoom: 20,
    key: 'G',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 't',
    icon: '!icon-hiking',
    url: '//tiles.freemap.sk/trails/{z}/{x}/{y}.png',
    attribution: [
      FM_ATTR,
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    minZoom: 8,
    maxNativeZoom: 16,
    key: 'T',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 'c',
    icon: 'bicycle',
    url: '//tiles.freemap.sk/cycle/{z}/{x}/{y}.png',
    attribution: [
      FM_ATTR,
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    minZoom: 8,
    maxNativeZoom: 16,
    key: 'C',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 'q',
    icon: 'snowflake-o',
    url: '//www.opensnowmap.org/pistes/{z}/{x}/{y}.png',
    attribution: [
      {
        type: 'map',
        name: '© OpenSnowMap.org',
      },
      OSM_DATA_ATTR,
    ],
    minZoom: 0,
    maxNativeZoom: 18,
    key: 'S',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 'r',
    icon: 'pencil-square-o',
    url: '//old.freemap.sk/layers/renderedby/?/{z}/{x}/{y}',
    minZoom: 8,
    maxNativeZoom: 12,
    key: 'R',
    showOnlyInExpertMode: true,
    zIndex: 4,
    attribution: [
      FM_ATTR,
    ],
  },
];
<commit_msg>fixed OSM_MAP_ATTR<commit_after>const OSM_MAP_ATTR = {
  type: 'map',
  name: '© OpenStreetMap',
  url: 'https://osm.org/',
};

const OSM_DATA_ATTR = {
  type: 'data',
  nameKey: 'mapLayers.attr.osmData',
  url: 'https://osm.org/copyright',
};

const FM_ATTR = {
  type: 'map',
  nameKey: 'mapLayers.attr.freemap',
};

const SRTM_ATTR = {
  type: 'data',
  nameKey: 'mapLayers.attr.srtm',
};

const STRAVA_ATTR = {
  type: 'map',
  name: '© Strava',
  url: 'https://www.strava.com/',
};

const NLC_ATTR = {
  type: 'map',
  name: '© NLC Zvolen',
  url: 'http://www.nlcsk.org/',
};

const INFOMAPA_ATTR = {
  type: 'map',
  name: '© infomapa.sk',
  url: 'http://www.infomapa.sk/',
};

export const baseLayers = [
  ...[
    ['A', 'car'],
    ['T', '!icon-hiking'],
    ['C', 'bicycle'],
    ['K', '!icon-skier-skiing'],
  ].map(([type, icon]) => ({
    type,
    icon,
    url: `//{s}.freemap.sk/${type}/{z}/{x}/{y}.{tileFormat}`,
    attribution: [
      FM_ATTR,
      OSM_DATA_ATTR,
      type !== 'A' && SRTM_ATTR,
    ].filter(a => a),
    minZoom: 8,
    maxNativeZoom: 16,
    key: type.toLowerCase(),
  })),
  {
    type: 'S',
    icon: 'plane',
    minZoom: 0,
    maxNativeZoom: 18,
    key: 's',
    attribution: [
      {
        type: 'map',
        name: '© Bing',
      },
      {
        type: 'map',
        name: '© Earthstar Geographics SIO',
      },
      {
        type: 'map',
        name: '© 2017 Microsoft Corporation',
      },
    ],
  },
  {
    type: 'O',
    icon: 'globe',
    url: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    minZoom: 0,
    maxNativeZoom: 19,
    attribution: [
      OSM_MAP_ATTR,
      OSM_DATA_ATTR,
    ],
    key: 'o',
  },
  {
    type: 'M',
    url: 'http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png',
    minZoom: 3,
    maxNativeZoom: 18,
    icon: 'bicycle',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        name: '© Martin Tesař',
        url: 'mailto:smmtb@gmail.com',
      },
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    key: 'm',
  },
  {
    type: 'p',
    url: '//tile.opentopomap.org/{z}/{x}/{y}.png',
    minZoom: 3,
    maxNativeZoom: 17,
    icon: 'tree',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        name: '© OpenTopoMap',
        url: 'https://tile.opentopomap.org/about#verwendung',
      },
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    key: 'p',
  },
  {
    type: 'b',
    url: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
    minZoom: 0,
    maxNativeZoom: 20,
    icon: 'medkit',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        nameKey: 'mapLayers.attr.hot',
        url: 'http://hot.openstreetmap.org',
      },
      OSM_DATA_ATTR,
    ],
    key: 'b',
  },
  {
    type: 'd',
    url: '//tile.memomaps.de/tilegen/{z}/{x}/{y}.png',
    minZoom: 0,
    maxNativeZoom: 18,
    icon: 'bus',
    showOnlyInExpertMode: true,
    attribution: [
      {
        type: 'map',
        name: '© MeMoMaps',
        url: 'https://memomaps.de/en/',
      },
      OSM_DATA_ATTR,
    ],
    key: 'd',
  },
  {
    type: 'i',
    icon: 'info',
    url: 'http://{s}.infomapa.sk/0/{z}/{x}/{y}',
    minZoom: 8,
    maxNativeZoom: 18,
    showOnlyInExpertMode: true,
    attribution: [
      INFOMAPA_ATTR,
      OSM_DATA_ATTR,
    ],
    key: 'i',
  },
  {
    type: 'j',
    icon: 'info',
    url: 'http://{s}.infomapa.sk/1/{z}/{x}/{y}',
    minZoom: 8,
    maxNativeZoom: 18,
    showOnlyInExpertMode: true,
    attribution: [
      INFOMAPA_ATTR,
      OSM_DATA_ATTR,
    ],
    key: 'j',
  },
  {
    type: 'h',
    url: 'http://tms.freemap.sk/historicke/{z}/{x}/{y}.png',
    minNativeZoom: 12,
    maxNativeZoom: 12,
    icon: 'institution',
    showOnlyInExpertMode: true,
    attribution: [],
    key: 'h',
  },
];

export const overlayLayers = [
  {
    type: 'I',
    icon: 'picture-o',
    minZoom: 0,
    key: 'F',
    zIndex: 3,
    attribution: [
      {
        type: 'photos',
        name: 'CC-BY-SA',
      },
    ],
  },
  {
    type: 'n',
    icon: 'tree',
    url: '//tiles.freemap.sk/nlc2016/{z}/{x}/{y}.png',
    attribution: [NLC_ATTR],
    minZoom: 11,
    maxNativeZoom: 15,
    key: 'N',
    zIndex: 2,
    adminOnly: true,
  },
  {
    type: 'l',
    icon: 'tree',
    url: 'http://gpsteam.eu/cache/nlcml/{z}/{x}/{y}.png',
    attribution: [NLC_ATTR],
    minZoom: 14,
    maxNativeZoom: 16,
    key: 'L',
    zIndex: 2,
    adminOnly: true,
  },
  ...['both', 'ride', 'run', 'water', 'winter'].map(([type], i) => ({
    type: `s${i}`,
    icon: 'scribd', // TODO use correct logo
    url: `//heatmap-external-{s}.strava.com/tiles/${type}/bluered/{z}/{x}/{y}.png?px=256`,
    attribution: [STRAVA_ATTR],
    minZoom: 0,
    maxNativeZoom: 16,
    key: type === 'both' ? 'H' : undefined,
    showOnlyInExpertMode: true,
    zIndex: 2,
  })),
  {
    type: 'g',
    icon: '!icon-gps-device',
    url: '//gps-{s}.tile.openstreetmap.org/lines/{z}/{x}/{y}.png',
    attribution: [
      OSM_MAP_ATTR,
      OSM_DATA_ATTR,
    ],
    minZoom: 0,
    maxNativeZoom: 20,
    key: 'G',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 't',
    icon: '!icon-hiking',
    url: '//tiles.freemap.sk/trails/{z}/{x}/{y}.png',
    attribution: [
      FM_ATTR,
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    minZoom: 8,
    maxNativeZoom: 16,
    key: 'T',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 'c',
    icon: 'bicycle',
    url: '//tiles.freemap.sk/cycle/{z}/{x}/{y}.png',
    attribution: [
      FM_ATTR,
      OSM_DATA_ATTR,
      SRTM_ATTR,
    ],
    minZoom: 8,
    maxNativeZoom: 16,
    key: 'C',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 'q',
    icon: 'snowflake-o',
    url: '//www.opensnowmap.org/pistes/{z}/{x}/{y}.png',
    attribution: [
      {
        type: 'map',
        name: '© OpenSnowMap.org',
      },
      OSM_DATA_ATTR,
    ],
    minZoom: 0,
    maxNativeZoom: 18,
    key: 'S',
    showOnlyInExpertMode: true,
    zIndex: 2,
  },
  {
    type: 'r',
    icon: 'pencil-square-o',
    url: '//old.freemap.sk/layers/renderedby/?/{z}/{x}/{y}',
    minZoom: 8,
    maxNativeZoom: 12,
    key: 'R',
    showOnlyInExpertMode: true,
    zIndex: 4,
    attribution: [
      FM_ATTR,
    ],
  },
];
<|endoftext|>


<commit_before>/**
 * @author David Gossow - dgossow@willowgarage.com
 */

/**
 * A Transform in 3-space. Values are copied into this object.
 *
 * @constructor
 * @param options - possible keys include:
 *   * translation - the ROSLIB.Vector3 describing the translation
 *   * rotation - the ROSLIB.Quaternion describing the rotation
 *   * transform - another Transform to copy initial values from
 */
ROSLIB.Transform = function(options) {
  var options = options || {};
  if (options.transform) {
    this.translation = options.transform.translation.clone();
    this.rotation = options.transform.rotation.clone();
  } else {
    // copy the values into this object if they exist
    this.translation = new ROSLIB.Vector3({
      vector : options.translation
    });
    this.rotation = new ROSLIB.Quaternion({
      quaternion : options.rotation
    });
  }
};

/**
 * Apply a transform against the given ROSLIB.Pose.
 *
 * @param pose the pose to transform with
 * @returns a pointer to the pose
 */
ROSLIB.Transform.prototype.apply = function(pose) {
  pose.position.multiplyQuaternion(this.rotation);
  pose.position.add(this.translation);

  this.rotation.multiply(pose.orientation);
  return pose;
};

/**
 * Apply an inverse transform against the given ROSLIB.Pose.
 *
 * @param pose the pose to transform with
 * @returns a pointer to the pose
 */
ROSLIB.Transform.prototype.applyInverse = function(pose) {
  var rotInv = this.rotation.clone().inverse();
  rotInv.multiplyVec3(pose.position);
  pose.position.sub(pose.position, this.translation);
  pose.orientation.multiply(rotInv, pose.orientation);
  return pose;
};

/**
 * Clone a copy of this transform.
 *
 * @returns the cloned transform
 */
ROSLIB.Transform.prototype.clone = function() {
  return new ROSLIB.Transform({
    transform : this
  });
};
<commit_msg>Transform moved to Math<commit_after>/**
 * @author David Gossow - dgossow@willowgarage.com
 */

/**
 * A Transform in 3-space. Values are copied into this object.
 *
 *  @constructor
 *  @param translation - the ROSLIB.Vector3 describing the translation
 *  @param rotation - the ROSLIB.Quaternion describing the rotation
 */
ROSLIB.Transform = function(translation, rotation) {
  this.translation = new ROSLIB.Vector3();
  this.rotation = new ROSLIB.Quaternion();
  if (translation !== undefined) {
    this.translation.copy(translation);
  }
  if (rotation !== undefined) {
    this.rotation.copy(rotation);
  }
};

/**
 * Apply a transform against the given ROSLIB.Pose.
 *
 * @param pose the pose to transform with
 * @returns a pointer to the pose
 */
ROSLIB.Transform.prototype.apply = function(pose) {
  this.rotation.multiplyVec3(pose.position);
  pose.position.add(pose.position, this.translation);
  pose.orientation.multiply(this.rotation, pose.orientation);
  return pose;
};

/**
 * Apply an inverse transform against the given ROSLIB.Pose.
 *
 * @param pose the pose to transform with
 * @returns a pointer to the pose
 */
ROSLIB.Transform.prototype.applyInverse = function(pose) {
  var rotInv = this.rotation.clone().inverse();
  rotInv.multiplyVec3(pose.position);
  pose.position.sub(pose.position, this.translation);
  pose.orientation.multiply(rotInv, pose.orientation);
  return pose;
};

/**
 * Copy the values from the given transform into this transform.
 *
 * @param transform the transform to copy
 * @returns a pointer to this transform
 */
ROSLIB.Transform.prototype.copy = function(transform) {
  transform.translation.copy(transform.translation);
  transform.rotation.copy(transform.rotation);
  return transform;
};

<|endoftext|>


<commit_before>YUI().use('view','panel', 'event-custom','event-focus','node-event-simulate','array-extras', function(Y) {
///start

var IO = YUI.namespace('Themer.IO');

Themer.appView = Y.Base.create('appView', Y.View, [], {

    container: Y.one('body'),

    //These events are app wide - hooked off body
    events: {
        //General External Link handler. Open in browser window.
        'a.external': { click: function(e) {
            e.halt(true);
            Titanium.Platform.openURL(e.currentTarget.get('href'));

            //from bootstrap :/ because we halt the event,
            //we need to manually close the dropdown. grrr...
            $('[data-toggle="dropdown"]').parent().removeClass('open');
        }},
        'a#reload': { click: function(e) {
            e.halt(true);
            window.location.reload(true);
        }},

        '.add-shop' : { click: 'addShop'},
        'ul.themes li': { contextmenu: function(e) {
            e.stopPropagation();

            var li = e.currentTarget,
                currentMenu = Ti.UI.createMenu(),
                shops = this.shops;

            var openFolder = Ti.UI.createMenuItem('Open In Finder', function() {
                li.one('.path').simulate('click');
            });

            var forceDeploy = Ti.UI.createMenuItem('Force Deploy', function() {
                Ti.API.warn('Force Deploy TK');

                if(confirm("Are you sure you want to upload this theme?")) {
                    var themeId = li.get('id').replace('theme-', '');

                    //go through the shops, look for the theme model of interest
                    shops.some(function(shopModel) {
                        var themeModel = shopModel.themes.getById(themeId);
                        if(themeModel) {
                            uploadThemeActivity(themeModel);
                            IO.deployTheme(shopModel, themeModel);
                            return true;
                        }
                        return false;
                    });
                }

            });

            currentMenu.appendItem(openFolder);
            currentMenu.appendItem(forceDeploy);
            Ti.UI.setContextMenu(currentMenu);
        }}
    },

    initializer: function() {
        console.log('appView: Initializer');

        var shops = this.shops = new Themer.shopList();
        shops.after('add', this.add, this);
        shops.after('remove', this.remove, this);

        //Reset also fires on initial model list load.
        shops.after('reset', this.render, this);

        shops.load();

        //Custom event that gets the new shop data from form
        //Creates the redirect URL to get the perms, and sends user there
        Y.on('addShopOk', function(shopData) {
            console.log('appView: addShopOk');
            // Ti.API.warn(shopData);
            connectingPanel().show();
            //Spin down any watchers
            Y.Global.fire('watch:killall');
            window.location = IO.authUrl(shopData.id);
            // this.shops.create(shopData);
        }, this);
    },

    render: function(e) {
        console.log('appView: render');

        if(this.shops.isEmpty()) {
            console.log('No Shops! Show Onboard!');
            Y.one('#no-shops').removeClass('hide');
            this.splash();
            // this.addShop();
            return this;
        }

        Y.one('#no-shops').addClass('hide');

        var fragment = Y.one(Y.config.doc.createDocumentFragment());

        Y.Array.each(e.models, function (model, index) {
            console.log('render shop id:' + model.get('id'));
            var view = new Themer.ShopView({
                model: model,
                container: Y.Lang.sub(Y.one('#shop-template').getContent(), {
                    store: model.get('id'),
                    'class': (index == 0) ? 'active':''
                })
            });

            fragment.append(view.render().container);
        });

        this.container.one('#shops').setContent(fragment);

        return this;
    },

    splash: function() {
        this.splashPanel = splashPanel();
        this.splashPanel.show();
    },

    // Click handler for the add shop button
    addShop: function(e) {
        //Setup the Add Shop form overlay
        this.addShopForm = this.addShopForm || this.createAddShopPanel();
        this.addShopForm.show();

        if(this.splashPanel) {
            this.splashPanel.hide();
        }

    },

    //Called when shop added to the shops list
    add: function(e) {
        console.log('appView: New Shop Added');

        //If its showing, hide it...
        if(this && this.splashPanel){ this.splashPanel.hide(); }
        if(this && this.addShopForm){ this.addShopForm.hide(); }
        Y.one('#no-shops').addClass('hide');
        var view = new Themer.ShopView({
            model: e.model,
            container: Y.Lang.sub(
                Y.one('#shop-template').getContent(), {store: e.model.get('id'), 'class': ''}
            )
        });

        this.container.one('#shops').append(view.render().container);

        view.chooseTheme();

    },

    remove: function(e) {
        console.log('appView: Shop Removed');
        if(this.shops.isEmpty()) {
            Y.one('#no-shops').removeClass('hide');
        } else {
            Y.Global.fire('shop:switch', {
              container: Y.one('#shops'),
              currentTarget: Y.one('#'+this.shops.item(0).get('id'))
            });
        }
    },

    createAddShopPanel: function() {

        var currentShops = this.shops;

        var panel = new Y.Panel({
            srcNode: '#add-shop-panel',
            width: 500,
            centered: true,
            visible: false,
            modal: true,
            headerContent: '
Add a New Shop
',
            zIndex: 10
        });

        var submitHandler = function(e) {
             e.preventDefault();

             //Step through and remove all unwanted crap that may be submitted
             var sanitizeShopId = function(str) {
                 var sid = str.replace('http://', '', 'i')
                           .replace('https://', '', 'i') //In case someone uses https://
                           .replace('.myshopify.com', '', 'i')
                           .replace('/',''); //#fixes public issue #10 - trailing slash

                return Y.Lang.trim(sid);
             };

             var data = {
                 id: sanitizeShopId(Y.one('input[name=id]').get('value'))
             };

             if((data.id == '') || (currentShops.getById(data.id) != null)) {
                 Y.one('input[name=id]').set('value','');
                 panel.hide();
             }
             else {
                 //Does this already exist?
                 Y.fire('addShopOk', data);
             }
        };

        panel.addButton({
            value: 'Cancel',
            action: function(e) {
                e.preventDefault();
                panel.hide();
            },
            classNames: 'btn',
            section: Y.WidgetStdMod.FOOTER
        });

        panel.get('contentBox').one('form').on('submit', submitHandler);
        panel.addButton({
             id: 'addShopOk',
             value: 'Add Shop',
             action: submitHandler,
             classNames: 'btn btn-primary',
             section: Y.WidgetStdMod.FOOTER
         });

        panel.render();
        Y.one('#add-shop-panel').removeClass('hide');

        return panel;
    }

});

Themer.ShopView = Y.Base.create('shopView', Y.View, [], {

    //Will pass in a custom container at instantiation
    //

    // container: Y.one('#shop-template').getContent(),
    template: '{store}',

    initializer: function() {
        console.log('ShopView: initializer');
        var model = this.model;
        //@todo clean up themes when shop destroyed
        // model.after('destroy', this.destroy, this);

        var themes = model.themes = new Themer.themeList();
        themes.parent_id = model.get('id');

        themes.after('add', this.addTheme, this);
        // themes.after('remove', this.remove, this);

        themes.load();

    },

    render: function() {
        Y.log('Render Shopview');
        var container = this.container,
            model = this.model,
            store = this.model.get('id');

        container.setContent(Y.Lang.sub(this.template, {
            store: store
        }));

        //Create the Theme container, since its not created with the shop anymore
        //(its in a different part of the dom...)
        Y.one('div#themes-container').append(Y.Lang.sub(
            Y.one('#shop-themes-wrapper').getContent(), {
                store: store,
                'class': (container.hasClass('active')) ? 'theme-show' : 'theme-hide'
            }
        ));

        //Handle no themes on the active theme created at init...
        if(model.themes.isEmpty() && container.hasClass('active')) {
          Y.one('div#themes-'+store).append(Y.one('#no-themes'));
          Y.one('#no-themes').removeClass('hide');
        }

        //If themes, empty, does nothing, so we dont bother with if/else
        model.themes.each(function(theme) {
            // console.log(item.toJSON());
            var view = new Themer.ThemeView({
                model: theme
            });

            Y.one('div#themes-'+store+" .theme-list").append(view.render().container);
            // fragment.append(view.render().container);
        });

        //add delegate to listen for Add Theme button press
        Y.one('div#themes-'+store).delegate('click',
            this.chooseTheme,
            '.add-theme',
            {'model': model} //Add some context
        );

        Y.one('div#themes-'+store).delegate('click',
            this.remove,
            '.remove-shop',
            this //Add some context
        );

        return this;
    },

    remove: function(e) {
        console.log('ShopView:remove');
        //Delete the associated themes list view
        Y.one('#themes-'+this.model.get('id')).remove(true);

        this.constructor.superclass.remove.call(this);
        this.model.destroy({'delete': true});
    },

    //Called when theme added to the shop themes list
    addTheme: function(e) {
        console.log('shopView: New Theme Added');
        var shop = this.model,
            theme = e.model,
            themeList = Y.one('#themes-'+shop.get('id')+' .theme-list');

        var view = new Themer.ThemeView({
            model: theme
        });

        var existing = themeList.one('#theme-'+theme.get('id')),
            newNode = view.render().container;

        if(existing) {
            themeList.replaceChild(newNode, existing);
        } else {
            themeList.append(newNode);
        }

        Y.one('#no-themes').addClass('hide');

        //Throw up activity indicator.
        var panel = downloadThemeActivity(theme);

        //Create folder, if it doesn't exist
        var destinationDir = Titanium.Filesystem.getFile(theme.get('path'));
        if( (destinationDir.exists() == false) && (destinationDir.createDirectory() == false)) {
            alert('We could not create the download directory.');
            return;
        }

        //Before download, kill off any watchers on that theme
        //otherwise we will have a right mess
        Themer.Watcher.kill(theme.get('id'));

        //Begin download.
        IO.downloadTheme(shop, theme);

        Y.Global.on('download:done', function(e) {
            //Spin up a watcher
            Themer.Watcher.start(shop, theme);
        });
    },

    chooseTheme: function() {
        console.log('ShopView:chooseTheme');
        var ThisShopModel = this.model,
            shopWorkingThemes = ThisShopModel.themes;

        //Open Panel
        var panel = createThemePicker();
        //Fetch Themes
        IO.fetchThemesList(ThisShopModel, {

            success: function(resp) {
                // console.log('Success!');
                // console.log(resp);

                var result = JSON.parse(resp.responseText),
                    themeList = Y.Node.create('

');

                result.themes.forEach(function(item) {
                    var li = Y.Lang.sub('
{name} [{role}]
', item);
                    themeList.append(li);
                });

                themeList.delegate('click', function(e) {
                    // console.log(e);
                    var selectedId = e.currentTarget.get('id').replace('theme-', '');

                    var selectedTheme = Y.Array.find(result.themes, function(item) {
                        return (item.id == selectedId);
                    });
                    selectedTheme.parent_id = ThisShopModel.get('id');

                    panel.hide();
                    panel.destroy();

                    //Show folder picker
                    Titanium.UI.currentWindow.openFolderChooserDialog(function(dir) {
                            if(dir.length == 0) { return false; }
                            selectedTheme.path = dir[0].concat(Ti.Filesystem.getSeparator(), selectedTheme.parent_id, '-', selectedTheme.id);
                            shopWorkingThemes.create(selectedTheme);
                            return true;
                        },
                        {
                            title: 'Choose Download Location',
                            multiple:false,
                            directories:true,
                            files:false
                        }
                    );

                }, 'li');

                panel.set('bodyContent', themeList);
                panel.set('centered', true); //to re-center
            },

            failure: function(resp) {
                console.log('Failure!');
                console.log(resp);
                var result = JSON.parse(resp.responseText);
                panel.set('bodyContent', result.errors || "Unknown Error");
            }
        });
        //On selection of theme, prompt with folder.

    }
});

var createThemePicker = function(shopModel) {

    var panel = new Y.Panel({
        width: 400,
        centered: true,
        visible: true,
        modal: true,
        headerContent: '
Choose a theme
',
        zIndex: 10,
        bodyContent: '
Loading themes for this Shop... Just a moment please
'
    });

    panel.render();

    return panel;

};

var downloadThemeActivity = function(themeModel) {

    var panel = new Y.Panel({
        width: 500,
        centered: true,
        visible: true,
        modal: true,
        buttons: [], //no close button
        headerContent: '
Download theme: '+ themeModel.get('name')+'
',
        zIndex: 10,
        bodyContent: '
Downloading files to:
'+ themeModel.get('path')+'

(^v^)/'
    });

    panel.addButton({
        value: 'Cancel',
        action: function(e) {
            e.preventDefault();
            Y.Global.fire('download:cancel');
        },
        classNames: 'btn',
        section: Y.WidgetStdMod.FOOTER
    });

    panel.render();
    panel.set('centered', true); //to re-center post render
    var assetDlHandle = Y.Global.on('asset:download', function(e) {
        Y.one('#downstatus').setContent(e.asset + '...');
    });

    var ddHandle = Y.Global.once('download:done', function(e) {
        //cleanup
        assetDlHandle.detach();

        e = e || {};
        var cancelled = e.cancelled || false;
        panel.hide();
        var gmsg = {
            title: 'Download Done',
            message: 'The theme '+themeModel.get('name')+' is done downloading'
        };

        if(cancelled){
            gmsg = {
                title: 'Download Cancelled',
                message: 'Some files have been downloaded.'
            };
        }
        growl(gmsg);
        cleanUp();
    });

    var deHandle = Y.Global.once('download:error', function(e) {
        panel.hide();
        cleanUp();
    });

    var cleanUp = function() {
        ddHandle.detach();
        deHandle.detach();
        assetDlHandle.detach();
    };

    return panel;

};

var splashPanel = function() {

    var panel = new Y.Panel({
        width: 700,
        height: 450,
        centered: true,
        visible: true,
        modal: true,
        buttons: [], //no close button
        zIndex: 12,
        srcNode: '#splash-page'
    });

    panel.render();
    Y.one('#splash-page').removeClass('hide');

    panel.set('centered', true); //to re-center

    return panel;

};

var connectingPanel = function() {

    var panel = new Y.Panel({
        width: 300,
        centered: true,
        visible: true,
        modal: true,
        buttons: [], //no close button
        zIndex: 12,
        srcNode: '#connecting-shopify-panel'
    });

    panel.render();
    Y.one('#connecting-shopify-panel').removeClass('hide');

    panel.set('centered', true); //to re-center

    return panel;

};

///end
});
<commit_msg>remove dead code<commit_after>YUI().use('view','panel', 'event-custom','event-focus','node-event-simulate','array-extras', function(Y) {
///start

var IO = YUI.namespace('Themer.IO');

Themer.appView = Y.Base.create('appView', Y.View, [], {

    container: Y.one('body'),

    //These events are app wide - hooked off body
    events: {
        //General External Link handler. Open in browser window.
        'a.external': { click: function(e) {
            e.halt(true);
            Titanium.Platform.openURL(e.currentTarget.get('href'));

            //from bootstrap :/ because we halt the event,
            //we need to manually close the dropdown. grrr...
            $('[data-toggle="dropdown"]').parent().removeClass('open');
        }},
        'a#reload': { click: function(e) {
            e.halt(true);
            window.location.reload(true);
        }},

        '.add-shop' : { click: 'addShop'}
    },

    initializer: function() {
        console.log('appView: Initializer');

        var shops = this.shops = new Themer.shopList();
        shops.after('add', this.add, this);
        shops.after('remove', this.remove, this);

        //Reset also fires on initial model list load.
        shops.after('reset', this.render, this);

        shops.load();

        //Custom event that gets the new shop data from form
        //Creates the redirect URL to get the perms, and sends user there
        Y.on('addShopOk', function(shopData) {
            console.log('appView: addShopOk');
            // Ti.API.warn(shopData);
            connectingPanel().show();
            //Spin down any watchers
            Y.Global.fire('watch:killall');
            window.location = IO.authUrl(shopData.id);
            // this.shops.create(shopData);
        }, this);
    },

    render: function(e) {
        console.log('appView: render');

        if(this.shops.isEmpty()) {
            console.log('No Shops! Show Onboard!');
            Y.one('#no-shops').removeClass('hide');
            this.splash();
            // this.addShop();
            return this;
        }

        Y.one('#no-shops').addClass('hide');

        var fragment = Y.one(Y.config.doc.createDocumentFragment());

        Y.Array.each(e.models, function (model, index) {
            console.log('render shop id:' + model.get('id'));
            var view = new Themer.ShopView({
                model: model,
                container: Y.Lang.sub(Y.one('#shop-template').getContent(), {
                    store: model.get('id'),
                    'class': (index == 0) ? 'active':''
                })
            });

            fragment.append(view.render().container);
        });

        this.container.one('#shops').setContent(fragment);

        return this;
    },

    splash: function() {
        this.splashPanel = splashPanel();
        this.splashPanel.show();
    },

    // Click handler for the add shop button
    addShop: function(e) {
        //Setup the Add Shop form overlay
        this.addShopForm = this.addShopForm || this.createAddShopPanel();
        this.addShopForm.show();

        if(this.splashPanel) {
            this.splashPanel.hide();
        }

    },

    //Called when shop added to the shops list
    add: function(e) {
        console.log('appView: New Shop Added');

        //If its showing, hide it...
        if(this && this.splashPanel){ this.splashPanel.hide(); }
        if(this && this.addShopForm){ this.addShopForm.hide(); }
        Y.one('#no-shops').addClass('hide');
        var view = new Themer.ShopView({
            model: e.model,
            container: Y.Lang.sub(
                Y.one('#shop-template').getContent(), {store: e.model.get('id'), 'class': ''}
            )
        });

        this.container.one('#shops').append(view.render().container);

        view.chooseTheme();

    },

    remove: function(e) {
        console.log('appView: Shop Removed');
        if(this.shops.isEmpty()) {
            Y.one('#no-shops').removeClass('hide');
        } else {
            Y.Global.fire('shop:switch', {
              container: Y.one('#shops'),
              currentTarget: Y.one('#'+this.shops.item(0).get('id'))
            });
        }
    },

    createAddShopPanel: function() {

        var currentShops = this.shops;

        var panel = new Y.Panel({
            srcNode: '#add-shop-panel',
            width: 500,
            centered: true,
            visible: false,
            modal: true,
            headerContent: '
Add a New Shop
',
            zIndex: 10
        });

        var submitHandler = function(e) {
             e.preventDefault();

             //Step through and remove all unwanted crap that may be submitted
             var sanitizeShopId = function(str) {
                 var sid = str.replace('http://', '', 'i')
                           .replace('https://', '', 'i') //In case someone uses https://
                           .replace('.myshopify.com', '', 'i')
                           .replace('/',''); //#fixes public issue #10 - trailing slash

                return Y.Lang.trim(sid);
             };

             var data = {
                 id: sanitizeShopId(Y.one('input[name=id]').get('value'))
             };

             if((data.id == '') || (currentShops.getById(data.id) != null)) {
                 Y.one('input[name=id]').set('value','');
                 panel.hide();
             }
             else {
                 //Does this already exist?
                 Y.fire('addShopOk', data);
             }
        };

        panel.addButton({
            value: 'Cancel',
            action: function(e) {
                e.preventDefault();
                panel.hide();
            },
            classNames: 'btn',
            section: Y.WidgetStdMod.FOOTER
        });

        panel.get('contentBox').one('form').on('submit', submitHandler);
        panel.addButton({
             id: 'addShopOk',
             value: 'Add Shop',
             action: submitHandler,
             classNames: 'btn btn-primary',
             section: Y.WidgetStdMod.FOOTER
         });

        panel.render();
        Y.one('#add-shop-panel').removeClass('hide');

        return panel;
    }

});

Themer.ShopView = Y.Base.create('shopView', Y.View, [], {

    //Will pass in a custom container at instantiation
    //

    // container: Y.one('#shop-template').getContent(),
    template: '{store}',

    initializer: function() {
        console.log('ShopView: initializer');
        var model = this.model;
        //@todo clean up themes when shop destroyed
        // model.after('destroy', this.destroy, this);

        var themes = model.themes = new Themer.themeList();
        themes.parent_id = model.get('id');

        themes.after('add', this.addTheme, this);
        // themes.after('remove', this.remove, this);

        themes.load();

    },

    render: function() {
        Y.log('Render Shopview');
        var container = this.container,
            model = this.model,
            store = this.model.get('id');

        container.setContent(Y.Lang.sub(this.template, {
            store: store
        }));

        //Create the Theme container, since its not created with the shop anymore
        //(its in a different part of the dom...)
        Y.one('div#themes-container').append(Y.Lang.sub(
            Y.one('#shop-themes-wrapper').getContent(), {
                store: store,
                'class': (container.hasClass('active')) ? 'theme-show' : 'theme-hide'
            }
        ));

        //Handle no themes on the active theme created at init...
        if(model.themes.isEmpty() && container.hasClass('active')) {
          Y.one('div#themes-'+store).append(Y.one('#no-themes'));
          Y.one('#no-themes').removeClass('hide');
        }

        //If themes, empty, does nothing, so we dont bother with if/else
        model.themes.each(function(theme) {
            // console.log(item.toJSON());
            var view = new Themer.ThemeView({
                model: theme
            });

            Y.one('div#themes-'+store+" .theme-list").append(view.render().container);
            // fragment.append(view.render().container);
        });

        //add delegate to listen for Add Theme button press
        Y.one('div#themes-'+store).delegate('click',
            this.chooseTheme,
            '.add-theme',
            {'model': model} //Add some context
        );

        Y.one('div#themes-'+store).delegate('click',
            this.remove,
            '.remove-shop',
            this //Add some context
        );

        return this;
    },

    remove: function(e) {
        console.log('ShopView:remove');
        //Delete the associated themes list view
        Y.one('#themes-'+this.model.get('id')).remove(true);

        this.constructor.superclass.remove.call(this);
        this.model.destroy({'delete': true});
    },

    //Called when theme added to the shop themes list
    addTheme: function(e) {
        console.log('shopView: New Theme Added');
        var shop = this.model,
            theme = e.model,
            themeList = Y.one('#themes-'+shop.get('id')+' .theme-list');

        var view = new Themer.ThemeView({
            model: theme
        });

        var existing = themeList.one('#theme-'+theme.get('id')),
            newNode = view.render().container;

        if(existing) {
            themeList.replaceChild(newNode, existing);
        } else {
            themeList.append(newNode);
        }

        Y.one('#no-themes').addClass('hide');

        //Throw up activity indicator.
        var panel = downloadThemeActivity(theme);

        //Create folder, if it doesn't exist
        var destinationDir = Titanium.Filesystem.getFile(theme.get('path'));
        if( (destinationDir.exists() == false) && (destinationDir.createDirectory() == false)) {
            alert('We could not create the download directory.');
            return;
        }

        //Before download, kill off any watchers on that theme
        //otherwise we will have a right mess
        Themer.Watcher.kill(theme.get('id'));

        //Begin download.
        IO.downloadTheme(shop, theme);

        Y.Global.on('download:done', function(e) {
            //Spin up a watcher
            Themer.Watcher.start(shop, theme);
        });
    },

    chooseTheme: function() {
        console.log('ShopView:chooseTheme');
        var ThisShopModel = this.model,
            shopWorkingThemes = ThisShopModel.themes;

        //Open Panel
        var panel = createThemePicker();
        //Fetch Themes
        IO.fetchThemesList(ThisShopModel, {

            success: function(resp) {
                // console.log('Success!');
                // console.log(resp);

                var result = JSON.parse(resp.responseText),
                    themeList = Y.Node.create('

');

                result.themes.forEach(function(item) {
                    var li = Y.Lang.sub('
{name} [{role}]
', item);
                    themeList.append(li);
                });

                themeList.delegate('click', function(e) {
                    // console.log(e);
                    var selectedId = e.currentTarget.get('id').replace('theme-', '');

                    var selectedTheme = Y.Array.find(result.themes, function(item) {
                        return (item.id == selectedId);
                    });
                    selectedTheme.parent_id = ThisShopModel.get('id');

                    panel.hide();
                    panel.destroy();

                    //Show folder picker
                    Titanium.UI.currentWindow.openFolderChooserDialog(function(dir) {
                            if(dir.length == 0) { return false; }
                            selectedTheme.path = dir[0].concat(Ti.Filesystem.getSeparator(), selectedTheme.parent_id, '-', selectedTheme.id);
                            shopWorkingThemes.create(selectedTheme);
                            return true;
                        },
                        {
                            title: 'Choose Download Location',
                            multiple:false,
                            directories:true,
                            files:false
                        }
                    );

                }, 'li');

                panel.set('bodyContent', themeList);
                panel.set('centered', true); //to re-center
            },

            failure: function(resp) {
                console.log('Failure!');
                console.log(resp);
                var result = JSON.parse(resp.responseText);
                panel.set('bodyContent', result.errors || "Unknown Error");
            }
        });
        //On selection of theme, prompt with folder.

    }
});

var createThemePicker = function(shopModel) {

    var panel = new Y.Panel({
        width: 400,
        centered: true,
        visible: true,
        modal: true,
        headerContent: '
Choose a theme
',
        zIndex: 10,
        bodyContent: '
Loading themes for this Shop... Just a moment please
'
    });

    panel.render();

    return panel;

};

var downloadThemeActivity = function(themeModel) {

    var panel = new Y.Panel({
        width: 500,
        centered: true,
        visible: true,
        modal: true,
        buttons: [], //no close button
        headerContent: '
Download theme: '+ themeModel.get('name')+'
',
        zIndex: 10,
        bodyContent: '
Downloading files to:
'+ themeModel.get('path')+'

(^v^)/'
    });

    panel.addButton({
        value: 'Cancel',
        action: function(e) {
            e.preventDefault();
            Y.Global.fire('download:cancel');
        },
        classNames: 'btn',
        section: Y.WidgetStdMod.FOOTER
    });

    panel.render();
    panel.set('centered', true); //to re-center post render
    var assetDlHandle = Y.Global.on('asset:download', function(e) {
        Y.one('#downstatus').setContent(e.asset + '...');
    });

    var ddHandle = Y.Global.once('download:done', function(e) {
        //cleanup
        assetDlHandle.detach();

        e = e || {};
        var cancelled = e.cancelled || false;
        panel.hide();
        var gmsg = {
            title: 'Download Done',
            message: 'The theme '+themeModel.get('name')+' is done downloading'
        };

        if(cancelled){
            gmsg = {
                title: 'Download Cancelled',
                message: 'Some files have been downloaded.'
            };
        }
        growl(gmsg);
        cleanUp();
    });

    var deHandle = Y.Global.once('download:error', function(e) {
        panel.hide();
        cleanUp();
    });

    var cleanUp = function() {
        ddHandle.detach();
        deHandle.detach();
        assetDlHandle.detach();
    };

    return panel;

};

var splashPanel = function() {

    var panel = new Y.Panel({
        width: 700,
        height: 450,
        centered: true,
        visible: true,
        modal: true,
        buttons: [], //no close button
        zIndex: 12,
        srcNode: '#splash-page'
    });

    panel.render();
    Y.one('#splash-page').removeClass('hide');

    panel.set('centered', true); //to re-center

    return panel;

};

var connectingPanel = function() {

    var panel = new Y.Panel({
        width: 300,
        centered: true,
        visible: true,
        modal: true,
        buttons: [], //no close button
        zIndex: 12,
        srcNode: '#connecting-shopify-panel'
    });

    panel.render();
    Y.one('#connecting-shopify-panel').removeClass('hide');

    panel.set('centered', true); //to re-center

    return panel;

};

///end
});
<|endoftext|>


<commit_before>const webpack = require('webpack');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const AngularGetTextPlugin = require('./angular-gettext-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const utils = require('./webpack.utils');

const scssPath = path.resolve('./src/');
const imagesPath = path.resolve('./src/images');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    path: utils.formatPath('.'),
    publicPath: '',
    filename: 'scripts/[name].bundle.js?[hash]',
    chunkFilename: 'scripts/[name].js?[chunkhash]',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    alias: {
      '@waldur': path.resolve('./src/'),
      sass: path.resolve('./src/sass/'),
    }
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'cache-loader',
          },
          {
            loader: 'babel-loader',
          },
        ],
      },
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'cache-loader',
          },
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true
            },
          },
        ],
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
            options: {
              minimize: true,
            },
          },
        ],
      },
      {
        test: /\.scss$/,
        use: [
          utils.isProd ? 'style-loader' : MiniCssExtractPlugin.loader,
          utils.isProd ? 'css-loader': 'css-loader?sourceMap',
          utils.isProd ? 'postcss-loader': 'postcss-loader?sourceMap',
          utils.isProd ? 'sass-loader?includePaths[]=' + scssPath : 'sass-loader?sourceMap&includePaths[]=' + scssPath,
        ]
      },
      {
        test: /\.css$/,
        use: [
          utils.isProd ? 'style-loader' : MiniCssExtractPlugin.loader,
          utils.isProd ? 'css-loader' : 'css-loader?sourcemap',
        ],
      },
      {
        test: /\.font\.js/,
        use: [
          utils.isProd ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'webfonts-loader',
            options: {
              embed: utils.isProd,
            },
          },
        ],
      },
      {
        test: /\.(eot|svg|otf|ttf|woff|woff2)(\?v=\d+\.\d+\.\d+)?/,
        use: [
          {
            loader: 'file-loader',
            options: {
              publicPath: '../',
              name: 'fonts/[name].[ext]?[hash]'
            }
          },
        ],
      },
      {
        test: /\.(png|jpg|jpeg|gif|ico)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              publicPath: '../',
              name: 'images/[name].[ext]?[hash]'
            },
          },
        ],
      },
      {
        test: /\.md$/,
        use: [
          {
            loader: 'html-loader',
          },
          {
            loader: 'markdown-loader',
          },
        ]
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index-template.html',
      filename: utils.formatPath('index.html'),
      inject: 'body',
      chunks: ['index'],
      alwaysWriteToDisk: true,
      chunksSortMode: function(a, b) {
        return (a.names[0] < b.names[0]) ? 1 : -1;
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name]-bundle.css?[contenthash]'
    }),
    // Moment locales extraction
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /(az|en-gb|et|ru|lt|lv)/),
    // some files are not referenced explicitly, copy them.
    new CopyWebpackPlugin([
      {from: './src/views', to: utils.formatPath('./views')},
      {from: path.resolve(imagesPath, './appstore'), to: utils.formatPath('images/appstore')},
      {from: path.resolve(imagesPath, './help'), to: utils.formatPath('images/help')},
      {from: path.resolve(imagesPath, './waldur'), to: utils.formatPath('images/waldur')},
      {from: path.resolve(imagesPath, './service-providers'), to: utils.formatPath('images/service-providers')},
      // favicon is a part of white-labeling, store such resources separately.
      // https://opennode.atlassian.net/wiki/display/WD/HomePort+configuration#HomePortconfiguration-White-labeling
      {from: path.resolve(imagesPath, './favicon.ico'), to: utils.formatPath('images/favicon.ico'), toType: 'file'},
      // manifest.json is an experimental feature that is currently breaking caching
      // {from:  './app/manifest.json', to: utils.formatPath('manifest.json'), toType: 'file'},
    ]),
    // Internationalization plugin, extracts strings for translation, and generates JSON dictionaries based on the already translated ones.
    new AngularGetTextPlugin({
      compileTranslations: {
        input: path.resolve('./i18n/*.po'),
        outputFolder: 'scripts/i18n',
        format: 'json',
      },
      extractStrings: {
        patterns: [
          'src/**/*.html',
          'src/**/*.js',
          'src/**/*.jsx',
          'src/**/*.ts',
          'src/**/*.tsx',
        ],
        destination: path.resolve('./i18n/template.pot'),
        lineNumbers: false,
        markerNames: ['gettext', 'translate']
      }
    }),
  ],
  stats: {
    children: false,
    hash: false,
    version: false,
    warnings: false,
    errorDetails: true,
  }
};
<commit_msg>Use MiniCssExtractPlugin loader in production.<commit_after>const webpack = require('webpack');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const AngularGetTextPlugin = require('./angular-gettext-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const utils = require('./webpack.utils');

const scssPath = path.resolve('./src/');
const imagesPath = path.resolve('./src/images');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    path: utils.formatPath('.'),
    publicPath: '',
    filename: 'scripts/[name].bundle.js?[hash]',
    chunkFilename: 'scripts/[name].js?[chunkhash]',
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    alias: {
      '@waldur': path.resolve('./src/'),
      sass: path.resolve('./src/sass/'),
    }
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'cache-loader',
          },
          {
            loader: 'babel-loader',
          },
        ],
      },
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'cache-loader',
          },
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true
            },
          },
        ],
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
            options: {
              minimize: true,
            },
          },
        ],
      },
      {
        test: /\.scss$/,
        use: [
          utils.isProd ? MiniCssExtractPlugin.loader : 'style-loader',
          utils.isProd ? 'css-loader': 'css-loader?sourceMap',
          utils.isProd ? 'postcss-loader': 'postcss-loader?sourceMap',
          utils.isProd ? 'sass-loader?includePaths[]=' + scssPath : 'sass-loader?sourceMap&includePaths[]=' + scssPath,
        ]
      },
      {
        test: /\.css$/,
        use: [
          utils.isProd ? MiniCssExtractPlugin.loader : 'style-loader',
          utils.isProd ? 'css-loader' : 'css-loader?sourcemap',
        ],
      },
      {
        test: /\.font\.js/,
        use: [
          utils.isProd ? MiniCssExtractPlugin.loader : 'style-loader',
          'css-loader',
          {
            loader: 'webfonts-loader',
            options: {
              embed: utils.isProd,
            },
          },
        ],
      },
      {
        test: /\.(eot|svg|otf|ttf|woff|woff2)(\?v=\d+\.\d+\.\d+)?/,
        use: [
          {
            loader: 'file-loader',
            options: {
              publicPath: '../',
              name: 'fonts/[name].[ext]?[hash]'
            }
          },
        ],
      },
      {
        test: /\.(png|jpg|jpeg|gif|ico)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              publicPath: '../',
              name: 'images/[name].[ext]?[hash]'
            },
          },
        ],
      },
      {
        test: /\.md$/,
        use: [
          {
            loader: 'html-loader',
          },
          {
            loader: 'markdown-loader',
          },
        ]
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index-template.html',
      filename: utils.formatPath('index.html'),
      inject: 'body',
      chunks: ['index'],
      alwaysWriteToDisk: true,
      chunksSortMode: function(a, b) {
        return (a.names[0] < b.names[0]) ? 1 : -1;
      }
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name]-bundle.css?[contenthash]'
    }),
    // Moment locales extraction
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /(az|en-gb|et|ru|lt|lv)/),
    // some files are not referenced explicitly, copy them.
    new CopyWebpackPlugin([
      {from: './src/views', to: utils.formatPath('./views')},
      {from: path.resolve(imagesPath, './appstore'), to: utils.formatPath('images/appstore')},
      {from: path.resolve(imagesPath, './help'), to: utils.formatPath('images/help')},
      {from: path.resolve(imagesPath, './waldur'), to: utils.formatPath('images/waldur')},
      {from: path.resolve(imagesPath, './service-providers'), to: utils.formatPath('images/service-providers')},
      // favicon is a part of white-labeling, store such resources separately.
      // https://opennode.atlassian.net/wiki/display/WD/HomePort+configuration#HomePortconfiguration-White-labeling
      {from: path.resolve(imagesPath, './favicon.ico'), to: utils.formatPath('images/favicon.ico'), toType: 'file'},
      // manifest.json is an experimental feature that is currently breaking caching
      // {from:  './app/manifest.json', to: utils.formatPath('manifest.json'), toType: 'file'},
    ]),
    // Internationalization plugin, extracts strings for translation, and generates JSON dictionaries based on the already translated ones.
    new AngularGetTextPlugin({
      compileTranslations: {
        input: path.resolve('./i18n/*.po'),
        outputFolder: 'scripts/i18n',
        format: 'json',
      },
      extractStrings: {
        patterns: [
          'src/**/*.html',
          'src/**/*.js',
          'src/**/*.jsx',
          'src/**/*.ts',
          'src/**/*.tsx',
        ],
        destination: path.resolve('./i18n/template.pot'),
        lineNumbers: false,
        markerNames: ['gettext', 'translate']
      }
    }),
  ],
  stats: {
    children: false,
    hash: false,
    version: false,
    warnings: false,
    errorDetails: true,
  }
};
<|endoftext|>


<commit_before>//
//  The code to drive the interactive page
//
var Lab = {
  //
  //  Defaults for the options
  //
  defaults: {
    collapse: true,
    width: 100,
    overflow: false,
    walker: "dummy",
    highlight: "none",
    background: "blue",
    foreground: "black"
  },
  // The assistive extension
  ASSISTIVE: null,
  //
  //  The TeX code for the examples menu
  //
  Examples: [],

  //
  //  Typeset the math from the text area
  //
  Typeset: function () {
    if (this.jax) {
      var math = this.input.value;
      MathJax.Hub.Queue(
        ["Disable",this.SMML],
        ["Text",this.jax[0],math],
        ["Enable",this.SMML],
        ["Text",this.jax[1],math],
        ["ShowMathML",this],
        ["CollapseWideMath",MathJax.Extension.SemanticCollapse]
      );
    }
  },

  //
  //  Encode the TeX and add it to the URL so that reloading the page
  //  keeps the same TeX in place (so when you edit the code, you don't
  //  have to retype the TeX to view it again).
  //
  Keep: function () {
    window.location =
      String(window.location).replace(/\?.*/,"")+"?"
        +[this.example.value, this.width.value, this.renderer.value,
          this.collapse, this.overflow,
          Lab.ASSISTIVE.config.walker,
          Lab.ASSISTIVE.config.highlight,
          Lab.ASSISTIVE.config.background,
          Lab.ASSISTIVE.config.foreground,
          ""].join(';')
        +escape(this.input.value);
  },

  //
  //  Select a specific example equation
  //
  Select: function (n) {
    if (n === "-" || n === "?") return;
    this.input.value = this.Examples[n];
    this.Typeset();
  },

  //
  //  Show the enhanced MathML
  //
  ShowMathML: function () {
    this.mathml.innerHTML = "";
    MathJax.HTML.addText(this.mathml,this.jax[1].root.toMathML().replace(/data-semantic-/g,""));
  },

  //
  //  Check for RETURN with any meta key as an alternative to clicking
  //  the TYPESET button
  //
  CheckKey: function (event) {
    if (!event) event = window.event;
    var code = event.which || event.keyCode;
    if ((event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) &&
        (code === 13 || code === 10)) {
      if (event.preventDefault) event.preventDefault();
      event.returnValue = false;
      this.Typeset();
    }
  },

  //
  //  Set the width of the output div
  //
  setWidth: function (width,skipHandler) {
    this.container.style.width = width+"%";
    document.getElementById("range_output").innerHTML = width+"%";
    if (!skipHandler) MathJax.Extension.SemanticCollapse.resizeHandler({});
  },

  //
  //  The collapse toggle
  //
  collapse: true,
  setCollapse: function (type,skipUpdate) {
    this.collapse = type;
    MathJax.Extension.SemanticCollapse[type ? "Enable" : "Disable"]();
    if (!skipUpdate) {
      MathJax.Hub.Queue(
        ["Reprocess",this.jax[1]],
        ["ShowMathML",this],
        ["CollapseWideMath",MathJax.Extension.SemanticCollapse]
      );
    }
  },

  //
  //  The overflow toggle
  //
  overflow: false,
  setOverflow: function (type,skipUpdate) {
    this.overflow = type;
    if (!skipUpdate) MathJax.Hub.Queue(["Rerender",MathJax.Hub]);
  },
  NewMath: function (message) {
    if (!this.overflow) return;
    var jax = MathJax.Hub.getJaxFor(message[1]);
    if (jax.root.Get("display") === "block") {
      var div = jax.SourceElement().previousSibling;
      div.style.overflow = "auto";
      div.style.minHeight = (div.offsetHeight+1) + "px"; // force height to be big enough
    }
  },

  //
  //  The renderer selection
  //
  setRenderer: function (mode,skipUpdate) {
    MathJax.Hub.Queue(["setRenderer",MathJax.Hub,mode,"jax/mml"]);
    if (!skipUpdate) MathJax.Hub.Queue(["Rerender",MathJax.Hub]);
  },

  //
  //  The static highlight selection
  //
  explorerOptions: [],
  executeExplorerOptions: function() {
    while (Lab.ASSISTIVE && Lab.explorerOptions.length > 0) {
      Lab.ASSISTIVE.setOption.apply(Lab.ASSISTIVE,
                                    Lab.explorerOptions.pop());
    }
  },
  setExplorerOption: function(key, value) {
    Lab.explorerOptions.push([key, value]);
    Lab.executeExplorerOptions();
  },

  //
  //  Directly select a specific test equation
  //
  DirectSelect: function (n) {
    (n < 1 && (this.Current = this.Examples.length)) ||
      (n > this.Examples.length && (this.Current = 1)) ||
      (this.Current = parseInt(n));
    this.input.value = this.Examples[this.Current - 1];
    this.example.value = this.Current;
    this.Typeset();
  },
  //
  // Current test equation.
  //
  Current: 0,
  //
  // Select next test equation.
  //
  Next: function() {
    this.DirectSelect(this.Current + 1);
  },
  //
  // Select previous test equation.
  //
  Prev: function() {
    this.DirectSelect(this.Current - 1);
  },

  //
  //  Enable/Disable inputs
  //
  EnableInputs: function (enable) {
    var INPUTS = ["input","select","textarea"];
    for (var k = 0; k < INPUTS.length; k++) {
      var inputs = document.getElementsByTagName(INPUTS[k]);
      for (var i = 0, m = inputs.length; i < m; i++) inputs[i].disabled = !enable;
    }
  }
};

//
//  Hook into "New Math" signal to set overflow
//
MathJax.Hub.Register.MessageHook("New Math",["NewMath",Lab]);

//
// Wait for explorer to be ready and set its options
//
MathJax.Hub.Register.StartupHook("Explorer Ready",
                                 function() {
                                   Lab.ASSISTIVE = MathJax.Extension.Assistive;
                                   Lab.executeExplorerOptions();
                                 });

//
//  Hook into menu renderer changes
//
MathJax.Hub.Register.StartupHook("MathMenu Ready",function () {
  MathJax.Extension.MathMenu.signal.Interest(function (message) {
    if (message[0] === "radio button") {
      var variable = message[1].variable;
      if (variable === 'renderer') {
        Lab.renderer.value = message[1].value;
        return;
      }
      if (String(variable).match(/^Assistive-/)) {
        var key = String(variable).replace('Assistive-', '');
        var element = document.getElementById(key);
        if (element) {
          element.value = message[1].value;
        }
      }
    }
  });
});

//
//  Initialize everything once MathJax has run the first time
//
MathJax.Hub.Register.StartupHook("onLoad",[Lab.EnableInputs,false]);
MathJax.Hub.Queue(function () {
  var defaults = [null,"0",
    String(Lab.defaults.width),
    Lab.defaults.renderer,
    String(Lab.defaults.collapse),
    String(Lab.defaults.overflow),
    Lab.defaults.walker,
    Lab.defaults.highlight,
    Lab.defaults.background,
    Lab.defaults.foreground,
    "",
  ];
  Lab.SMML = MathJax.Extension.SemanticMathML;
  Lab.jax = MathJax.Hub.getAllJax();
  Lab.input = document.getElementById("input");
  Lab.container = document.getElementById("container");
  Lab.enriched = document.getElementById("enriched");
  Lab.mathml = document.getElementById("mathml");
  Lab.example = document.getElementById("example");
  Lab.width = document.getElementById("width");
  Lab.renderer = document.getElementById("renderer");
  if (window.location.search.length > 1)
    defaults = window.location.search.match(
        /^\?(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*)$/);
  Lab.example.value = defaults[1];
  Lab.Current = parseInt(defaults[1]);
  Lab.width.value = defaults[2];
  Lab.enriched.style.width = defaults[2];
  Lab.renderer.value = defaults[3];
  Lab.setRenderer(Lab.renderer.value,true);
  Lab.renderer.onchange = function () {Lab.setRenderer(this.value)};
  Lab.setWidth(Lab.width.value,true);
  Lab.collapse = document.getElementById("collapse").checked = (defaults[4] === "true");
  Lab.setCollapse(Lab.collapse,true);
  Lab.overflow = document.getElementById("overflow").checked = (defaults[5] === "true");
  Lab.setOverflow(Lab.overflow,true);
  Lab.setExplorerOption("walker", document.getElementById("walker").value = defaults[6]);
  Lab.setExplorerOption("highlight", document.getElementById("highlight").value = defaults[7]);
  Lab.setExplorerOption("background", document.getElementById("background").value = defaults[8]);
  Lab.setExplorerOption("foreground", document.getElementById("foreground").value = defaults[9]);
  Lab.input.value = unescape(defaults[10]);
  MathJax.Extension.SemanticCollapse.config.disabled = !Lab.defaults.enableCollapse;
  if (Lab.input.value !== "") Lab.Typeset();
  Lab.EnableInputs(true);
});
<commit_msg>Gets Lab to display the correct MathML in case speech is (re)generated.<commit_after>//
//  The code to drive the interactive page
//
var Lab = {
  //
  //  Defaults for the options
  //
  defaults: {
    collapse: true,
    width: 100,
    overflow: false,
    walker: "dummy",
    highlight: "none",
    background: "blue",
    foreground: "black"
  },
  // The assistive extension
  ASSISTIVE: null,
  //
  //  The TeX code for the examples menu
  //
  Examples: [],

  //
  //  Typeset the math from the text area
  //
  Typeset: function () {
    if (this.jax) {
      var math = this.input.value;
      MathJax.Hub.Queue(
        ["Disable",this.SMML],
        ["Text",this.jax[0],math],
        ["Enable",this.SMML],
        ["Text",this.jax[1],math],
        ["ShowMathML",this],
        ["CollapseWideMath",MathJax.Extension.SemanticCollapse]
      );
    }
  },

  //
  //  Encode the TeX and add it to the URL so that reloading the page
  //  keeps the same TeX in place (so when you edit the code, you don't
  //  have to retype the TeX to view it again).
  //
  Keep: function () {
    window.location =
      String(window.location).replace(/\?.*/,"")+"?"
        +[this.example.value, this.width.value, this.renderer.value,
          this.collapse, this.overflow,
          Lab.ASSISTIVE.config.walker,
          Lab.ASSISTIVE.config.highlight,
          Lab.ASSISTIVE.config.background,
          Lab.ASSISTIVE.config.foreground,
          ""].join(';')
        +escape(this.input.value);
  },

  //
  //  Select a specific example equation
  //
  Select: function (n) {
    if (n === "-" || n === "?") return;
    this.input.value = this.Examples[n];
    this.Typeset();
  },

  //
  //  Show the enhanced MathML
  //
  ShowMathML: function () {
    this.mathml.innerHTML = "";
    MathJax.HTML.addText(this.mathml,this.jax[1].root.toMathML().replace(/data-semantic-/g,""));
  },

  //
  //  Check for RETURN with any meta key as an alternative to clicking
  //  the TYPESET button
  //
  CheckKey: function (event) {
    if (!event) event = window.event;
    var code = event.which || event.keyCode;
    if ((event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) &&
        (code === 13 || code === 10)) {
      if (event.preventDefault) event.preventDefault();
      event.returnValue = false;
      this.Typeset();
    }
  },

  //
  //  Set the width of the output div
  //
  setWidth: function (width,skipHandler) {
    this.container.style.width = width+"%";
    document.getElementById("range_output").innerHTML = width+"%";
    if (!skipHandler) MathJax.Extension.SemanticCollapse.resizeHandler({});
  },

  //
  //  The collapse toggle
  //
  collapse: true,
  setCollapse: function (type,skipUpdate) {
    this.collapse = type;
    MathJax.Extension.SemanticCollapse[type ? "Enable" : "Disable"]();
    if (!skipUpdate) {
      MathJax.Hub.Queue(
        ["Reprocess",this.jax[1]],
        ["CollapseWideMath",MathJax.Extension.SemanticCollapse]
      );
    }
  },

  //
  //  The overflow toggle
  //
  overflow: false,
  setOverflow: function (type,skipUpdate) {
    this.overflow = type;
    if (!skipUpdate) MathJax.Hub.Queue(["Rerender",MathJax.Hub]);
  },
  NewMath: function (message) {
    if (!this.overflow) return;
    var jax = MathJax.Hub.getJaxFor(message[1]);
    if (jax.root.Get("display") === "block") {
      var div = jax.SourceElement().previousSibling;
      div.style.overflow = "auto";
      div.style.minHeight = (div.offsetHeight+1) + "px"; // force height to be big enough
    }
  },

  //
  //  The renderer selection
  //
  setRenderer: function (mode,skipUpdate) {
    MathJax.Hub.Queue(["setRenderer",MathJax.Hub,mode,"jax/mml"]);
    if (!skipUpdate) MathJax.Hub.Queue(["Rerender",MathJax.Hub]);
  },

  //
  //  The static highlight selection
  //
  explorerOptions: [],
  executeExplorerOptions: function() {
    while (Lab.ASSISTIVE && Lab.explorerOptions.length > 0) {
      Lab.ASSISTIVE.setOption.apply(Lab.ASSISTIVE,
                                    Lab.explorerOptions.pop());
    }
  },
  setExplorerOption: function(key, value) {
    Lab.explorerOptions.push([key, value]);
    Lab.executeExplorerOptions();
  },

  //
  //  Directly select a specific test equation
  //
  DirectSelect: function (n) {
    (n < 1 && (this.Current = this.Examples.length)) ||
      (n > this.Examples.length && (this.Current = 1)) ||
      (this.Current = parseInt(n));
    this.input.value = this.Examples[this.Current - 1];
    this.example.value = this.Current;
    this.Typeset();
  },
  //
  // Current test equation.
  //
  Current: 0,
  //
  // Select next test equation.
  //
  Next: function() {
    this.DirectSelect(this.Current + 1);
  },
  //
  // Select previous test equation.
  //
  Prev: function() {
    this.DirectSelect(this.Current - 1);
  },

  //
  //  Enable/Disable inputs
  //
  EnableInputs: function (enable) {
    var INPUTS = ["input","select","textarea"];
    for (var k = 0; k < INPUTS.length; k++) {
      var inputs = document.getElementsByTagName(INPUTS[k]);
      for (var i = 0, m = inputs.length; i < m; i++) inputs[i].disabled = !enable;
    }
  }
};

//
//  Hook into "New Math" signal to set overflow
//
MathJax.Hub.Register.MessageHook("New Math",["NewMath",Lab]);

//
// Wait for explorer to be ready and set its options
//
MathJax.Hub.Register.StartupHook("Explorer Ready",
                                 function() {
                                   Lab.ASSISTIVE = MathJax.Extension.Assistive;
                                   Lab.executeExplorerOptions();
                                 });

//
// Hook into reprocess as this can be also triggered from the menu.
//
MathJax.Hub.Register.MessageHook('End Reprocess', ['ShowMathML', Lab]);

//
//  Hook into menu renderer changes
//
MathJax.Hub.Register.StartupHook("MathMenu Ready",function () {
  MathJax.Extension.MathMenu.signal.Interest(function (message) {
    if (message[0] === "radio button") {
      var variable = message[1].variable;
      if (variable === 'renderer') {
        Lab.renderer.value = message[1].value;
        return;
      }
      if (String(variable).match(/^Assistive-/)) {
        var key = String(variable).replace('Assistive-', '');
        var element = document.getElementById(key);
        if (element) {
          element.value = message[1].value;
        }
      }
    }
  });
});

//
//  Initialize everything once MathJax has run the first time
//
MathJax.Hub.Register.StartupHook("onLoad",[Lab.EnableInputs,false]);
MathJax.Hub.Queue(function () {
  var defaults = [null,"0",
    String(Lab.defaults.width),
    Lab.defaults.renderer,
    String(Lab.defaults.collapse),
    String(Lab.defaults.overflow),
    Lab.defaults.walker,
    Lab.defaults.highlight,
    Lab.defaults.background,
    Lab.defaults.foreground,
    "",
  ];
  Lab.SMML = MathJax.Extension.SemanticMathML;
  Lab.jax = MathJax.Hub.getAllJax();
  Lab.input = document.getElementById("input");
  Lab.container = document.getElementById("container");
  Lab.enriched = document.getElementById("enriched");
  Lab.mathml = document.getElementById("mathml");
  Lab.example = document.getElementById("example");
  Lab.width = document.getElementById("width");
  Lab.renderer = document.getElementById("renderer");
  if (window.location.search.length > 1)
    defaults = window.location.search.match(
        /^\?(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*?);(.*)$/);
  Lab.example.value = defaults[1];
  Lab.Current = parseInt(defaults[1]);
  Lab.width.value = defaults[2];
  Lab.enriched.style.width = defaults[2];
  Lab.renderer.value = defaults[3];
  Lab.setRenderer(Lab.renderer.value,true);
  Lab.renderer.onchange = function () {Lab.setRenderer(this.value)};
  Lab.setWidth(Lab.width.value,true);
  Lab.collapse = document.getElementById("collapse").checked = (defaults[4] === "true");
  Lab.setCollapse(Lab.collapse,true);
  Lab.overflow = document.getElementById("overflow").checked = (defaults[5] === "true");
  Lab.setOverflow(Lab.overflow,true);
  Lab.setExplorerOption("walker", document.getElementById("walker").value = defaults[6]);
  Lab.setExplorerOption("highlight", document.getElementById("highlight").value = defaults[7]);
  Lab.setExplorerOption("background", document.getElementById("background").value = defaults[8]);
  Lab.setExplorerOption("foreground", document.getElementById("foreground").value = defaults[9]);
  Lab.input.value = unescape(defaults[10]);
  MathJax.Extension.SemanticCollapse.config.disabled = !Lab.defaults.enableCollapse;
  if (Lab.input.value !== "") Lab.Typeset();
  Lab.EnableInputs(true);
});
<|endoftext|>