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

var dl = deeplearn;
var NDArrayMathGPU = dl.NDArrayMathGPU;
var Array3D = dl.Array3D;
var Array4D = dl.Array4D;
var Array1D = dl.Array1D;
var ENV = dl.ENV;

function computeWeightsShape4D(
    inputDepth, outputDepth, filterHeight,
    filterWidth) {
    return [filterHeight, filterWidth, inputDepth, outputDepth];
}

const canvas = document.getElementById(`plot`);
var chart = create_chart(canvas);

chartData = [{
        label: 'deeplearnjs -- t1',
        backgroundColor: window.chartColors.red,
        borderColor: window.chartColors.red,
        data: [],
        fill: false,
        pointRadius: 0,
        pointHitRadius: 5,
        borderWidth: 1,
        lineTension: 0,
    },
    {
        label: "convnetjs -- t2",
        backgroundColor: window.chartColors.blue,
        borderColor: window.chartColors.blue,
        data: [],
        fill: false,
        pointRadius: 0,
        pointHitRadius: 5,
        borderWidth: 1,
        lineTension: 0,
    }
]
config.data.datasets = chartData;
chart.update();

var table = document.getElementById(`divTable`);
init_table(table, ['Size', 't1 (ms)', 't2 (ms)']);

class ConvBenchmark {

    constructor(libName) {
        this.libName = libName
        this.btn = document.getElementById('buttontp' + "_" + this.libName);
        this.paused = true;
        this.btn.addEventListener('click', (event) => {
            this.toggle_pause();
            ga('send', 'event', 'deeplearn_conv_benchmark', 'click', `Run Benchmark ${this.libName}`, this.libName === 'dljs' ? 30 : 31);
        });
    }

    toggle_pause() {
        this.paused = !this.paused;
        if (this.paused) {
            this.btn.value = 'Run Benchmark'
            this.btn.disabled = false
        } else {
            this.btn.value = 'Running...';
            this.btn.disabled = true
        }
    }

    run(size, opType, params) {

        if (this.libName === 'dljs') {
            var math = new NDArrayMathGPU();
            var gpgpu = math.getGPGPUContext();
        }

        const inDepth = params.inDepth;
        const inShape = [size, size, inDepth];
        const outDepth = params.outDepth;
        const filterSize = params.filterSize;
        const stride = params.stride;
        let zeroPad = params.zeroPad;

        // outputRows>=0 needs to be asserted
        var outputRows = (inShape[0] - filterSize + 2 * zeroPad) / stride + 1;

        function isInt(a) {
            return a % 1 === 0;
        }

        if (outputRows <= 0) {
            console.log(`input size ${size} doesn't satisfy assertion outputRows>0, given inputRows=${size}, filterSize=${filterSize}, zeroPad=${zeroPad}, stride=${stride}, minimal size needs to be ${filterSize + 2 * zeroPad - stride}`)
        } else if (!isInt(outputRows)) {
            console.log(`outputRows ${outputRows} is not int`)
        }

        function find_min_pad(i, f, s) {

            for (let n = 0;; n++) {
                var z = (n * s - i + f) / 2;
                if (z > 0) {
                    console.log('found suitable z=' + z)
                    return z
                }
            }

        }
        zeroPad = find_min_pad(size, filterSize, stride);
        console.log(`min pad ${zeroPad} applied`)

        let benchmark;
        let out;
        let b;
        let x;

        if (this.libName === 'dljs') { //deeplearnjs

            if (opType === 'regular') {
                x = Array3D.randUniform(inShape, -1, 1);
                const wShape = computeWeightsShape4D(
                    inDepth, params.outDepth, filterSize, filterSize);
                var W = Array4D.randUniform(wShape, -1, 1);
                // const b = Array1D.randUniform([outDepth], -1, 1);

                benchmark = () => {
                    out = math.conv2d(x, W, null, stride, zeroPad); //bias=null, pad =0, this padding will be applied on four borders of input : left right top bottom
                }

            } else if (opType === 'transposed') {

                x = Array3D.randUniform([size, size, regParams.outDepth], -1, 1);
                const wShape = computeWeightsShape4D(
                    inDepth, params.outDepth, filterSize, filterSize);
                W = Array4D.randUniform(wShape, -1, 1);

                // no bias for conv transposed
                benchmark = () => {
                    out = math.conv2dTranspose(x, W, [size, size, inDepth], stride, pad);
                };

            } else if (opType === 'depthwise') {
                x = Array3D.randUniform(inShape, -1, 1);
                const wShape = computeWeightsShape4D(
                    inDepth, params.channelMul, filterSize, filterSize);
                W = Array4D.randUniform(wShape, -1, 1);

                // no bias for depth wise conv
                benchmark = () => {
                    out = math.depthwiseConv2D(x, W, stride, pad);
                };

            } else {
                throw new Error(`Unknown option ${opType}`);
            }

        } else { // convnetjs

            console.assert(opType === 'regular', `unsupported conv op type`)

            x = new convnetjs.Vol(size, size, inDepth); // 128,128,3
            const opt = {
                in_sx: size,
                in_sy: size,
                in_depth: inDepth,
                sx: filterSize,
                filters: outDepth,
                stride: stride,
                pad: zeroPad // this will be applied on four borders of input :left right top bottom
            }; // no bias, pad=0
            var layer = new convnetjs.ConvLayer(opt);

            benchmark = () => {
                out = layer.forward(x);
            }

        }

        const cleanup = () => {

            if (this.libName === 'dljs') {
                x.dispose();
                W.dispose();
                out.dispose();
                if (b != null) {
                    b.dispose();
                }
            } else {
                x = null;
                out = null;
            }

        };

        if (this.libName === 'dljs') {
            // Warmup.
            gpgpu.runQuery(benchmark);
            out.dispose();
        }

        let totalTime;

        if (this.libName === 'dljs') {

            if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE')) {
                totalTime = gpgpu.runQuery(benchmark);
            } else {
                const start = performance.now();

                benchmark();
                out.dataSync();

                totalTime = performance.now() - start;
            }
        } else {

            const start = performance.now();

            benchmark();

            totalTime = performance.now() - start;
        }

        console.log(`${this.libName} convolution output: ${out}`)
        cleanup();
        return totalTime;
    }

    run_test(params) {

        this.index = this.libName === 'dljs' ? 0 : 1;

        var bmrun = this;

        const runPromises = [];

        var sizes = [];
        for (let size = 16; size < 4000; size = size * 2) {
            var t = bmrun.run(size, 'regular', params)
            // t.then(data => console.log('conv on size', size, 'time', data, 'us'))
            runPromises.push(t);
            sizes.push(size)
        }

        if (config.data.labels.length !== sizes.length) {
            init_chart(config, sizes);
        }

        if (table.rows !== sizes.length + 1) {
            update_table_col(table, 0, sizes);
        }

        Promise.all(runPromises).then(results => {
            for (let i = 0; i < results.length; i++) {

                let resultString;
                let logString;
                let time = 0;
                let success = true;
                let size = sizes[i]
                try {
                    time = results[i];
                    resultString = time.toFixed(3) + ' ms';
                    logString = resultString;
                } catch (e) {
                    success = false;
                    resultString = 'Error';
                    logString = e.message;
                }

                if (time >= 0) {
                    if (success) {
                        chartData[bmrun.index].data.push(
                            // {
                            // x: size,
                            // y: time
                            // }
                            time
                        );

                    }
                    // rowValues.push(resultString);
                }
                console.log(`[${size}]: ${logString}`);

            }

            var resultStringArray = [];
            chartData[bmrun.index].data.forEach((time) => {
                resultStringArray.push(time.toFixed(3))
            })

            update_table_col(table, bmrun.index + 1, resultStringArray); // col0 is the size labels
            config.data.datasets = chartData;

            chart.update();

        });

        bmrun.toggle_pause();

    }
}

function init_chart(config, sizes) {

    config.data.labels = sizes;

    chart.update();

}

// (inputRows - fieldSize + 2 * zeroPad) / stride + 1) >=0 needs to be asserted
const convParams = {
    inDepth: 8,
    outDepth: 3,
    filterSize: 7,
    stride: 2,
    zeroPad: 0 // adjust zeroPad so that (inputSize - filterSize + 2* zeroPad) is integer
};

var bm_dljs = new ConvBenchmark('dljs');
var bm_cnjs = new ConvBenchmark('cnjs');

function run() {

    if (bm_dljs.paused === false) {

        setTimeout(function () {
            bm_dljs.run_test(convParams);
            run()
        }, 0);

    } else if (bm_cnjs.paused == false) {

        setTimeout(function () {
            bm_cnjs.run_test(convParams);
            run()
        }, 0);

    } else {
        setTimeout(function () {
            // console.log('outside')
            run();
        }, 1000);
    }

}

function start() {

    supported = detect_support();

    if (supported) {
        console.log('device & webgl supported')
        document.getElementById("buttontp_dljs").disabled = false;
    } else {
        console.log('device/webgl not supported')
        document.getElementById("buttontp_dljs").disabled = true;
    }

    run();
}<commit_msg>consistently format all float to fixed(3)<commit_after>/**
 * @license
 * Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */

var dl = deeplearn;
var NDArrayMathGPU = dl.NDArrayMathGPU;
var Array3D = dl.Array3D;
var Array4D = dl.Array4D;
var Array1D = dl.Array1D;
var ENV = dl.ENV;

function computeWeightsShape4D(
    inputDepth, outputDepth, filterHeight,
    filterWidth) {
    return [filterHeight, filterWidth, inputDepth, outputDepth];
}

const canvas = document.getElementById(`plot`);
var chart = create_chart(canvas);

chartData = [{
        label: 'deeplearnjs -- t1',
        backgroundColor: window.chartColors.red,
        borderColor: window.chartColors.red,
        data: [],
        fill: false,
        pointRadius: 0,
        pointHitRadius: 5,
        borderWidth: 1,
        lineTension: 0,
    },
    {
        label: "convnetjs -- t2",
        backgroundColor: window.chartColors.blue,
        borderColor: window.chartColors.blue,
        data: [],
        fill: false,
        pointRadius: 0,
        pointHitRadius: 5,
        borderWidth: 1,
        lineTension: 0,
    }
]
config.data.datasets = chartData;
chart.update();

var table = document.getElementById(`divTable`);
init_table(table, ['Size', 't1 (ms)', 't2 (ms)']);

class ConvBenchmark {

    constructor(libName) {
        this.libName = libName
        this.btn = document.getElementById('buttontp' + "_" + this.libName);
        this.paused = true;
        this.btn.addEventListener('click', (event) => {
            this.toggle_pause();
            ga('send', 'event', 'deeplearn_conv_benchmark', 'click', `Run Benchmark ${this.libName}`, this.libName === 'dljs' ? 30 : 31);
        });
    }

    toggle_pause() {
        this.paused = !this.paused;
        if (this.paused) {
            this.btn.value = 'Run Benchmark'
            this.btn.disabled = false
        } else {
            this.btn.value = 'Running...';
            this.btn.disabled = true
        }
    }

    run(size, opType, params) {

        if (this.libName === 'dljs') {
            var math = new NDArrayMathGPU();
            var gpgpu = math.getGPGPUContext();
        }

        const inDepth = params.inDepth;
        const inShape = [size, size, inDepth];
        const outDepth = params.outDepth;
        const filterSize = params.filterSize;
        const stride = params.stride;
        let zeroPad = params.zeroPad;

        // outputRows>=0 needs to be asserted
        var outputRows = (inShape[0] - filterSize + 2 * zeroPad) / stride + 1;

        function isInt(a) {
            return a % 1 === 0;
        }

        if (outputRows <= 0) {
            console.log(`input size ${size} doesn't satisfy assertion outputRows>0, given inputRows=${size}, filterSize=${filterSize}, zeroPad=${zeroPad}, stride=${stride}, minimal size needs to be ${filterSize + 2 * zeroPad - stride}`)
        } else if (!isInt(outputRows)) {
            console.log(`outputRows ${outputRows} is not int`)
        }

        function find_min_pad(i, f, s) {

            for (let n = 0;; n++) {
                var z = (n * s - i + f) / 2;
                if (z > 0) {
                    console.log('found suitable z=' + z)
                    return z
                }
            }

        }
        zeroPad = find_min_pad(size, filterSize, stride);
        console.log(`min pad ${zeroPad} applied`)

        let benchmark;
        let out;
        let b;
        let x;

        if (this.libName === 'dljs') { //deeplearnjs

            if (opType === 'regular') {
                x = Array3D.randUniform(inShape, -1, 1);
                const wShape = computeWeightsShape4D(
                    inDepth, params.outDepth, filterSize, filterSize);
                var W = Array4D.randUniform(wShape, -1, 1);
                // const b = Array1D.randUniform([outDepth], -1, 1);

                benchmark = () => {
                    out = math.conv2d(x, W, null, stride, zeroPad); //bias=null, pad =0, this padding will be applied on four borders of input : left right top bottom
                }

            } else if (opType === 'transposed') {

                x = Array3D.randUniform([size, size, regParams.outDepth], -1, 1);
                const wShape = computeWeightsShape4D(
                    inDepth, params.outDepth, filterSize, filterSize);
                W = Array4D.randUniform(wShape, -1, 1);

                // no bias for conv transposed
                benchmark = () => {
                    out = math.conv2dTranspose(x, W, [size, size, inDepth], stride, pad);
                };

            } else if (opType === 'depthwise') {
                x = Array3D.randUniform(inShape, -1, 1);
                const wShape = computeWeightsShape4D(
                    inDepth, params.channelMul, filterSize, filterSize);
                W = Array4D.randUniform(wShape, -1, 1);

                // no bias for depth wise conv
                benchmark = () => {
                    out = math.depthwiseConv2D(x, W, stride, pad);
                };

            } else {
                throw new Error(`Unknown option ${opType}`);
            }

        } else { // convnetjs

            console.assert(opType === 'regular', `unsupported conv op type`)

            x = new convnetjs.Vol(size, size, inDepth); // 128,128,3
            const opt = {
                in_sx: size,
                in_sy: size,
                in_depth: inDepth,
                sx: filterSize,
                filters: outDepth,
                stride: stride,
                pad: zeroPad // this will be applied on four borders of input :left right top bottom
            }; // no bias, pad=0
            var layer = new convnetjs.ConvLayer(opt);

            benchmark = () => {
                out = layer.forward(x);
            }

        }

        const cleanup = () => {

            if (this.libName === 'dljs') {
                x.dispose();
                W.dispose();
                out.dispose();
                if (b != null) {
                    b.dispose();
                }
            } else {
                x = null;
                out = null;
            }

        };

        if (this.libName === 'dljs') {
            // Warmup.
            gpgpu.runQuery(benchmark);
            out.dispose();
        }

        let totalTime;

        if (this.libName === 'dljs') {

            if (ENV.get('WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_RELIABLE')) {
                totalTime = gpgpu.runQuery(benchmark);
            } else {
                const start = performance.now();

                benchmark();
                out.dataSync();

                totalTime = performance.now() - start;
            }
        } else {

            const start = performance.now();

            benchmark();

            totalTime = performance.now() - start;
        }

        console.log(`${this.libName} convolution output: ${out}`)
        cleanup();
        return totalTime;
    }

    run_test(params) {

        this.index = this.libName === 'dljs' ? 0 : 1;

        var bmrun = this;

        const runPromises = [];

        var sizes = [];
        for (let size = 16; size < 4000; size = size * 2) {
            var t = bmrun.run(size, 'regular', params)
            // t.then(data => console.log('conv on size', size, 'time', data, 'us'))
            runPromises.push(t);
            sizes.push(size)
        }

        if (config.data.labels.length !== sizes.length) {
            init_chart(config, sizes);
        }

        if (table.rows !== sizes.length + 1) {
            update_table_col(table, 0, sizes);
        }

        Promise.all(runPromises).then(results => {
            for (let i = 0; i < results.length; i++) {

                let resultString;
                let logString;
                let time = 0;
                let success = true;
                let size = sizes[i]
                try {
                    time = results[i].toFixed(3);
                    resultString = time + ' ms';
                    logString = resultString;
                } catch (e) {
                    success = false;
                    resultString = 'Error';
                    logString = e.message;
                }

                if (time >= 0) {
                    if (success) {
                        chartData[bmrun.index].data.push(
                            // {
                            // x: size,
                            // y: time
                            // }
                            time
                        );

                    }
                    // rowValues.push(resultString);
                }
                console.log(`[${size}]: ${logString}`);

            }

            var resultStringArray = [];
            chartData[bmrun.index].data.forEach((time) => {
                resultStringArray.push(time);
            })

            update_table_col(table, bmrun.index + 1, resultStringArray); // col0 is the size labels
            config.data.datasets = chartData;

            chart.update();

        });

        bmrun.toggle_pause();

    }
}

function init_chart(config, sizes) {

    config.data.labels = sizes;

    chart.update();

}

// (inputRows - fieldSize + 2 * zeroPad) / stride + 1) >=0 needs to be asserted
const convParams = {
    inDepth: 8,
    outDepth: 3,
    filterSize: 7,
    stride: 2,
    zeroPad: 0 // adjust zeroPad so that (inputSize - filterSize + 2* zeroPad) is integer
};

var bm_dljs = new ConvBenchmark('dljs');
var bm_cnjs = new ConvBenchmark('cnjs');

function run() {

    if (bm_dljs.paused === false) {

        setTimeout(function () {
            bm_dljs.run_test(convParams);
            run()
        }, 0);

    } else if (bm_cnjs.paused == false) {

        setTimeout(function () {
            bm_cnjs.run_test(convParams);
            run()
        }, 0);

    } else {
        setTimeout(function () {
            // console.log('outside')
            run();
        }, 1000);
    }

}

function start() {

    supported = detect_support();

    if (supported) {
        console.log('device & webgl supported')
        document.getElementById("buttontp_dljs").disabled = false;
    } else {
        console.log('device/webgl not supported')
        document.getElementById("buttontp_dljs").disabled = true;
    }

    run();
}<|endoftext|>


<commit_before>'use strict';

var m = require('mithril');
var _ = require('lodash');

// global events
var EVENTS = require('../../events');

var Layer = function (data) {
    this.type = m.prop(data.type);
    this.l_id = m.prop(data.l_id);
    this.name = m.prop(data.name);
    this.visible = m.prop(data.visible);
    this.transparency = m.prop(data.transparency);
    // should we query this layer 'GetFeatureInfo'
    this.query = m.prop(false);
    this.showLayerControl = m.prop(false);
    this.showLayerDetails = m.prop(false);
};

var Group = function (data) {
    this.type = m.prop(data.type);
    this.name = m.prop(data.name);
    this.visible = m.prop(data.visible);
    this.collapsed = m.prop(data.collapsed);
    this.layers = m.prop(data.layers);

    this.query = m.prop(false);
};

var VIEWMODEL = function (options) {
    this.init(options);
};

VIEWMODEL.prototype = {
    init: function (options) {
        var self = this;

        this.options = options;
        this.layers = this.options.layers;

        this.layerTree = [];

        this.dragged_item = m.prop();

        _.forEach(this.options.layer_tree, function (treeItem) {
            if (treeItem.layer) {
                var layer = self.options.layers[treeItem.layer];
                self.layerTree.push(new Layer({
                    'type': 'layer',
                    'l_id':treeItem.layer,
                    'name':layer.layer_name,
                    'visible': layer.visible,
                    'transparency': layer.transparency
                    })
                );
            }

            if (treeItem.group) {
                var group = treeItem.group;

                // read group layers
                var groupLayers = _.map(group.layers, function(groupLayer) {
                    var layer = self.options.layers[groupLayer.layer];
                    return new Layer({
                        'type': 'layer',
                        'l_id':groupLayer.layer,
                        'name':layer.layer_name,
                        'visible': layer.visible,
                        'transparency': layer.transparency
                    });
                });

                self.layerTree.push(new Group({
                    'type': 'group',
                    'name': group.name,
                    'visible': group.visible,
                    'layers': groupLayers,
                    'collapsed' : group.collapsed
                    })
                );
            }
        });

        // control has been initialized
        EVENTS.emit('layers.initialized', {
            'layers': this.getLayersParam(),
            'transparencies': this.getTransparencyParam()
        });

    },

    emitLayersUpdated: function() {
        EVENTS.emit('layers.updated', {
            'layers': this.getLayersParam(),
            'transparencies': this.getTransparencyParam()
        });
    },

    emitQueryLayersUpdated: function () {
        EVENTS.emit('query.layers.updated', {
            'query_layers': this.getQueryLayersParam()
        });
    },

    getLayersParam: function () {
        // return comma concatenated string of visible layers
        var visible_layers = [];

        for (var i = 0; i < this.layerTree.length; i++) {
            var treeItem = this.layerTree[i];
            if (treeItem.type() === 'layer') {
                if (treeItem.visible()) {
                    visible_layers.push(treeItem.l_id());
                }
            } else {
                for (var j = 0; j < treeItem.layers().length; j++) {
                    var groupLayer = treeItem.layers()[j];
                    if (groupLayer.visible()) {
                        visible_layers.push(groupLayer.l_id());
                    }
                }
            }
        }

        return visible_layers.join(',');
    },
    getTransparencyParam: function () {
        // return comma concatenated string of visible layers transparencies
        var layers_transparencies = [];

        for (var i = 0; i < this.layerTree.length; i++) {
            var treeItem = this.layerTree[i];
            if (treeItem.type() === 'layer') {
                if (treeItem.visible()) {
                    layers_transparencies.push(treeItem.transparency());
                }
            } else {
                for (var j = 0; j < treeItem.layers().length; j++) {
                    var groupLayer = treeItem.layers()[j];
                    if (groupLayer.visible()) {
                        layers_transparencies.push(groupLayer.transparency());
                    }
                }
            }
        }

        return layers_transparencies.join(',');
    },
    getQueryLayersParam: function () {
        // return comma concatenated string of queryable layers
        var query_layers = [];

        for (var i = 0; i < this.layerTree.length; i++) {
            var treeItem = this.layerTree[i];

            if (treeItem.type() === 'layer') {
                if (treeItem.visible() && treeItem.query()) {
                    query_layers.push(treeItem.l_id());
                }
            } else {
                for (var j = 0; j < treeItem.layers().length; j++) {
                    var groupLayer = treeItem.layers()[j];
                    if (groupLayer.visible() && groupLayer.query()) {
                        query_layers.push(groupLayer.l_id());
                    }
                }
            }
        }

        return query_layers.join(',');
    },

    sort: function (layers, dragged_item) {
        // set new layers
        this.layerTree = layers;
        // track dragging element
        this.dragged_item = m.prop(dragged_item);
    },

    ev_dragStart: function(e) {
        // Fix for Firefox (maybe others), prevents dragstart event bubbling
        // on range input elements
        if (document.activeElement.type === 'range') {
            return false; // block dragging
        }

        // get the index (position in a list) of the dragged element
        this.vm.cur_dragged = Number(e.currentTarget.dataset.id);
        e.dataTransfer.effectAllowed = 'move';

        // HACK: don't show dragging ghost image
        var canvas = document.createElement('canvas');
        canvas.width = canvas.height = 1;
        var ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, 1, 1);
        e.dataTransfer.setDragImage(canvas, 0, 0);

        // we need to set some data to trigger ondragover element
        e.dataTransfer.setData('text/html', undefined);

    },

    ev_dragOver: function(e) {
        // element that's being currently dragged over
        var over = e.currentTarget;

        var dragged_item = this.vm.dragged_item();

        var previous_pos = isFinite(dragged_item) ? dragged_item : this.vm.cur_dragged;
        var next_pos = Number(over.dataset.id);

        if((e.clientY - over.offsetTop) > (over.offsetHeight / 2)) {
            next_pos++;
        }
        if(previous_pos < next_pos) {
            next_pos--;
        }

        var layers = this.vm.layerTree;
        layers.splice(next_pos, 0, layers.splice(previous_pos, 1)[0]);
        this.vm.sort(layers, next_pos);

        // it's important to prevent event bubbling as it would trigger ondragstart
        // when dragging over 'draggable' elements
        return false;
    },

    ev_dragEnd: function() {
        this.vm.sort(this.vm.layerTree, undefined);

        this.vm.emitLayersUpdated();
    },

    ev_layerToggle: function (item) {
        if (item.visible()) {
            item.visible(false);
        } else {
            item.visible(true);
        }
        this.vm.emitLayersUpdated();
    },

    ev_queryLayerToggle: function (item) {
        if (item.query()) {
            item.query(false);
        } else {
            item.query(true);
        }
        this.vm.emitQueryLayersUpdated();
    },

    ev_queryGroupToggle: function(item) {
        if (item.query()) {
            item.query(false);

            _.forEach(item.layers(), function (layer) {
                layer.query(false);
            });
        } else {
            item.query(true);

            _.forEach(item.layers(), function (layer) {
                layer.query(true);
            });
        }
        this.vm.emitQueryLayersUpdated();
    },

    ev_groupToggle: function(item) {
        if (item.visible()) {
            item.visible(false);

            _.forEach(item.layers(), function (layer) {
                layer.visible(false);
            });
        } else {
            item.visible(true);

            _.forEach(item.layers(), function (layer) {
                layer.visible(true);
            });
        }

        this.vm.emitLayersUpdated();
    },

    ev_layerTransparency: function (item, e) {
        item.transparency(e.target.value);

        this.vm.emitLayersUpdated();
    },

    ev_mouseOver: function(item) {
        item.showLayerControl(true);
    },

    ev_mouseOut: function(item) {
        item.showLayerControl(false);
    },

    ev_toggleShowControl: function(item) {
        if (item.showLayerDetails()) {
            item.showLayerDetails(false);
        } else {
            item.showLayerDetails(true);
        }
    },

    ev_toggleGroupCollapse: function(item) {
        if (item.collapsed()) {
            item.collapsed(false);
        } else {
            item.collapsed(true);
        }
    }
};

module.exports = VIEWMODEL;
<commit_msg>Toggle query only if a layer is visible<commit_after>'use strict';

var m = require('mithril');
var _ = require('lodash');

// global events
var EVENTS = require('../../events');

var Layer = function (data) {
    this.type = m.prop(data.type);
    this.l_id = m.prop(data.l_id);
    this.name = m.prop(data.name);
    this.visible = m.prop(data.visible);
    this.transparency = m.prop(data.transparency);
    // should we query this layer 'GetFeatureInfo'
    this.query = m.prop(false);
    this.showLayerControl = m.prop(false);
    this.showLayerDetails = m.prop(false);
};

var Group = function (data) {
    this.type = m.prop(data.type);
    this.name = m.prop(data.name);
    this.visible = m.prop(data.visible);
    this.collapsed = m.prop(data.collapsed);
    this.layers = m.prop(data.layers);

    this.query = m.prop(false);
};

var VIEWMODEL = function (options) {
    this.init(options);
};

VIEWMODEL.prototype = {
    init: function (options) {
        var self = this;

        this.options = options;
        this.layers = this.options.layers;

        this.layerTree = [];

        this.dragged_item = m.prop();

        _.forEach(this.options.layer_tree, function (treeItem) {
            if (treeItem.layer) {
                var layer = self.options.layers[treeItem.layer];
                self.layerTree.push(new Layer({
                    'type': 'layer',
                    'l_id':treeItem.layer,
                    'name':layer.layer_name,
                    'visible': layer.visible,
                    'transparency': layer.transparency
                    })
                );
            }

            if (treeItem.group) {
                var group = treeItem.group;

                // read group layers
                var groupLayers = _.map(group.layers, function(groupLayer) {
                    var layer = self.options.layers[groupLayer.layer];
                    return new Layer({
                        'type': 'layer',
                        'l_id':groupLayer.layer,
                        'name':layer.layer_name,
                        'visible': layer.visible,
                        'transparency': layer.transparency
                    });
                });

                self.layerTree.push(new Group({
                    'type': 'group',
                    'name': group.name,
                    'visible': group.visible,
                    'layers': groupLayers,
                    'collapsed' : group.collapsed
                    })
                );
            }
        });

        // control has been initialized
        EVENTS.emit('layers.initialized', {
            'layers': this.getLayersParam(),
            'transparencies': this.getTransparencyParam()
        });

    },

    emitLayersUpdated: function() {
        EVENTS.emit('layers.updated', {
            'layers': this.getLayersParam(),
            'transparencies': this.getTransparencyParam()
        });
    },

    emitQueryLayersUpdated: function () {
        EVENTS.emit('query.layers.updated', {
            'query_layers': this.getQueryLayersParam()
        });
    },

    getLayersParam: function () {
        // return comma concatenated string of visible layers
        var visible_layers = [];

        for (var i = 0; i < this.layerTree.length; i++) {
            var treeItem = this.layerTree[i];
            if (treeItem.type() === 'layer') {
                if (treeItem.visible()) {
                    visible_layers.push(treeItem.l_id());
                }
            } else {
                for (var j = 0; j < treeItem.layers().length; j++) {
                    var groupLayer = treeItem.layers()[j];
                    if (groupLayer.visible()) {
                        visible_layers.push(groupLayer.l_id());
                    }
                }
            }
        }

        return visible_layers.join(',');
    },
    getTransparencyParam: function () {
        // return comma concatenated string of visible layers transparencies
        var layers_transparencies = [];

        for (var i = 0; i < this.layerTree.length; i++) {
            var treeItem = this.layerTree[i];
            if (treeItem.type() === 'layer') {
                if (treeItem.visible()) {
                    layers_transparencies.push(treeItem.transparency());
                }
            } else {
                for (var j = 0; j < treeItem.layers().length; j++) {
                    var groupLayer = treeItem.layers()[j];
                    if (groupLayer.visible()) {
                        layers_transparencies.push(groupLayer.transparency());
                    }
                }
            }
        }

        return layers_transparencies.join(',');
    },
    getQueryLayersParam: function () {
        // return comma concatenated string of queryable layers
        var query_layers = [];

        for (var i = 0; i < this.layerTree.length; i++) {
            var treeItem = this.layerTree[i];

            if (treeItem.type() === 'layer') {
                if (treeItem.visible() && treeItem.query()) {
                    query_layers.push(treeItem.l_id());
                }
            } else {
                for (var j = 0; j < treeItem.layers().length; j++) {
                    var groupLayer = treeItem.layers()[j];
                    if (groupLayer.visible() && groupLayer.query()) {
                        query_layers.push(groupLayer.l_id());
                    }
                }
            }
        }

        return query_layers.join(',');
    },

    sort: function (layers, dragged_item) {
        // set new layers
        this.layerTree = layers;
        // track dragging element
        this.dragged_item = m.prop(dragged_item);
    },

    ev_dragStart: function(e) {
        // Fix for Firefox (maybe others), prevents dragstart event bubbling
        // on range input elements
        if (document.activeElement.type === 'range') {
            return false; // block dragging
        }

        // get the index (position in a list) of the dragged element
        this.vm.cur_dragged = Number(e.currentTarget.dataset.id);
        e.dataTransfer.effectAllowed = 'move';

        // HACK: don't show dragging ghost image
        var canvas = document.createElement('canvas');
        canvas.width = canvas.height = 1;
        var ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, 1, 1);
        e.dataTransfer.setDragImage(canvas, 0, 0);

        // we need to set some data to trigger ondragover element
        e.dataTransfer.setData('text/html', undefined);

    },

    ev_dragOver: function(e) {
        // element that's being currently dragged over
        var over = e.currentTarget;

        var dragged_item = this.vm.dragged_item();

        var previous_pos = isFinite(dragged_item) ? dragged_item : this.vm.cur_dragged;
        var next_pos = Number(over.dataset.id);

        if((e.clientY - over.offsetTop) > (over.offsetHeight / 2)) {
            next_pos++;
        }
        if(previous_pos < next_pos) {
            next_pos--;
        }

        var layers = this.vm.layerTree;
        layers.splice(next_pos, 0, layers.splice(previous_pos, 1)[0]);
        this.vm.sort(layers, next_pos);

        // it's important to prevent event bubbling as it would trigger ondragstart
        // when dragging over 'draggable' elements
        return false;
    },

    ev_dragEnd: function() {
        this.vm.sort(this.vm.layerTree, undefined);

        this.vm.emitLayersUpdated();
    },

    ev_layerToggle: function (item) {
        if (item.visible()) {
            item.visible(false);
            item.query(false);
        } else {
            item.visible(true);
        }
        this.vm.emitLayersUpdated();
        this.vm.emitQueryLayersUpdated();
    },

    ev_queryLayerToggle: function (item) {
        if (item.query()) {
            item.query(false);
        } else {
            if (item.visible()) {
                item.query(true);
            }
        }
        this.vm.emitQueryLayersUpdated();
    },

    ev_queryGroupToggle: function(item) {
        if (item.query()) {
            item.query(false);

            _.forEach(item.layers(), function (layer) {
                layer.query(false);
            });
        } else {
            if (item.visible()) {
                item.query(true);
            }

            _.forEach(item.layers(), function (layer) {
                if (layer.visible()) {
                    layer.query(true);
                }
            });
        }
        this.vm.emitQueryLayersUpdated();
    },

    ev_groupToggle: function(item) {
        if (item.visible()) {
            item.visible(false);

            _.forEach(item.layers(), function (layer) {
                layer.visible(false);
                layer.query(false);
            });
        } else {
            item.visible(true);

            _.forEach(item.layers(), function (layer) {
                layer.visible(true);
            });
        }

        this.vm.emitLayersUpdated();
        this.vm.emitQueryLayersUpdated();
    },

    ev_layerTransparency: function (item, e) {
        item.transparency(e.target.value);

        this.vm.emitLayersUpdated();
    },

    ev_mouseOver: function(item) {
        item.showLayerControl(true);
    },

    ev_mouseOut: function(item) {
        item.showLayerControl(false);
    },

    ev_toggleShowControl: function(item) {
        if (item.showLayerDetails()) {
            item.showLayerDetails(false);
        } else {
            item.showLayerDetails(true);
        }
    },

    ev_toggleGroupCollapse: function(item) {
        if (item.collapsed()) {
            item.collapsed(false);
        } else {
            item.collapsed(true);
        }
    }
};

module.exports = VIEWMODEL;
<|endoftext|>


<commit_before>/*global angular, define, console, window, navigator*/

define(function () {
    'use strict';

    function ctrl(HueService, $scope, DataService, $filter, PlaceholderDataUrl, $ionicModal, UtilityService, $state) {
        console.info("SceneModalCtrl init");

        DataService.getEnrichedLightInfos(false).then(function (data) {
            var tmp = [];
            angular.forEach(data, function (value, key) {
                value.id = key;
                tmp.push(value);
            });
            $scope.lights = tmp;
        });

        $scope.$on('modal.shown', function (event, modal) {
            if (modal.id === "sceneModal") {
                console.log("sceneModal shown");

                DataService.setSceneImageCropped(null);
                DataService.setSceneImage(null);

                $scope.isEditMode = (angular.isDefined($scope.scene) && $scope.scene !== null);
                if ($scope.isEditMode) {
                    console.log($scope.scene);
                }
                $scope.titleText = getTitleText();
                $scope.saveText = getSaveText();
                $scope.showCropButton = false;
                $scope.modalScene = {};
                $scope.modalScene.previewImage = getScenePreviewImage();
                $scope.modalScene.name = getName();
                $scope.modalScene.selectedLights = getSelectedLights();
            }
        });

        $scope.$on('modal.removed', function (event, modal) {
            if (modal.id === "sceneModalImageCrop") {
                if (isCroppedImageAvailable()) {
                    $scope.showCropButton = true;
                    $scope.modalScene.previewImage = DataService.getSceneImageCropped();
                }
            }
        });

        var isCroppedImageAvailable = function () {
            var croppedImageUrl = DataService.getSceneImageCropped();
            return croppedImageUrl !== null;
        };

        var getSelectedLights = function () {
            if (!$scope.isEditMode) {
                return {};
            }
            var tmp = {};
            angular.forEach($scope.scene.lights, function (lightId) {
                tmp[lightId] = true;
            });
            return tmp;
        };

        var getName = function () {
            if ($scope.isEditMode) {
                return $scope.scene.name;
            }
            return "";
        };

        var getTitleText = function () {
            if ($scope.isEditMode) {
                return $scope.scene.name;
            } else {
                return $filter('translate')('Home_LightList_CreateScene');
            }
        };

        var getSaveText = function () {
            if ($scope.isEditMode) {
                return $filter('translate')('SINGLE_Update');
            } else {
                return $filter('translate')('SINGLE_Create');
            }
        };

        var getScenePreviewImage = function () {
            if ($scope.isEditMode) {
                if (angular.isDefined($scope.allImages) && $scope.scene.imageId in $scope.allImages) {
                    return $scope.allImages[$scope.scene.imageId];
                }
            }
            return PlaceholderDataUrl;
        };

        $scope.closeModal = function () {
            $scope.sceneModal.hide();
        };

        $scope.deleteScene = function (sceneId) {
            console.log(sceneId);

            function onConfirm(buttonIndex) {
                if (buttonIndex === 1) //delete
                {
                    HueService.deleteScene(sceneId).then(function (data) {
                        DataService.removeCustomScene(sceneId);
                        $scope.refresh();
                        $scope.closeModal();
                    });
                }
            }

            navigator.notification.confirm(
                'Do you really want to delete the scene "' + $scope.scene.name + '"?', // message
                onConfirm, // callback to invoke with index of button pressed
                'Delete', // title
                    ['Delete', 'Cancel'] // buttonLabels
            );
        };

        $scope.createOrUpdateScene = function () {
            var tmp = [];

            angular.forEach($scope.modalScene.selectedLights, function (value, key) {
                if (value === true) {
                    tmp.push(key);
                }
            });
            if (tmp.length === 0) {
                navigator.notification.alert($filter('translate')('Select_At_Least_1_Light'), null, $filter('translate')('ERROR_TITLE'));
            } else if ($scope.modalScene.name.length === 0) {
                navigator.notification.alert($filter('translate')('Enter_Name'), null, $filter('translate')('ERROR_TITLE'));
            } else if ($scope.modalScene.name.length > 32) {
                navigator.notification.alert($filter('translate')('Name_Too_Long'), null, $filter('translate')('ERROR_TITLE'));
            }

            if (tmp.length > 0 && $scope.modalScene.name.length > 0 && $scope.modalScene.name.length <= 32) {
                if ($scope.isEditMode) {
                    HueService.modifyScene($scope.scene.id, $scope.modalScene.name, tmp).then(function (data) {
                        console.log(data);
                        DataService.removeCustomScene($scope.scene.id);
                        DataService.addCustomScene($scope.scene.id, $scope.modalScene.name, tmp, DataService.getSceneImageCropped());
                        $scope.closeModal();
                        if (angular.isDefined($scope.refresh)) {
                            $scope.refresh();
                        }
                        $state.go('main.home_tab.scenes');
                    });
                } else {
                    HueService.createScene($scope.modalScene.name, tmp).then(function (data) {
                        console.log(data);
                        DataService.addCustomScene(data[0].success.id, $scope.modalScene.name, tmp, DataService.getSceneImageCropped());
                        $scope.closeModal();
                        if (angular.isDefined($scope.refresh)) {
                            $scope.refresh();
                        }
                        $state.go('main.home_tab.scenes');
                    });
                }
            }
        };

        $scope.openSceneModalImageCrop = function () {
            $ionicModal.fromTemplateUrl('templates/home/modals/sceneModalImageCrop.html', {
                id: 'sceneModalImageCrop',
                scope: $scope,
                animation: 'slide-in-up'
            }).then(function (modal) {
                $scope.sceneModalImageCrop = modal;
                $scope.sceneModalImageCrop.show();
            });
        };

        $scope.scenePictureAlbum = function () {
            UtilityService.getPictureAlbumAsDataUrl().then(function (data) {
                DataService.setSceneImage(data);
                $scope.openSceneModalImageCrop();
            });
        };

        $scope.scenePictureCamera = function () {
            UtilityService.getPictureCameraAsDataUrl().then(function (data) {
                DataService.setSceneImage(data);
                $scope.openSceneModalImageCrop();
            });
        };
    }

    ctrl.$inject = ['HueService', '$scope', 'DataService', '$filter', 'PlaceholderDataUrl', '$ionicModal', 'UtilityService', '$state'];
    return ctrl;

});<commit_msg>bugfix edit scene images<commit_after>/*global angular, define, console, window, navigator*/

define(function () {
    'use strict';

    function ctrl(HueService, $scope, DataService, $filter, PlaceholderDataUrl, $ionicModal, UtilityService, $state) {
        console.info("SceneModalCtrl init");

        DataService.getEnrichedLightInfos(false).then(function (data) {
            var tmp = [];
            angular.forEach(data, function (value, key) {
                value.id = key;
                tmp.push(value);
            });
            $scope.lights = tmp;
        });

        $scope.$on('modal.shown', function (event, modal) {
            if (modal.id === "sceneModal") {
                console.log("sceneModal shown");

                DataService.setSceneImageCropped(null);
                DataService.setSceneImage(null);

                $scope.isEditMode = (angular.isDefined($scope.scene) && $scope.scene !== null);
                if ($scope.isEditMode) {
                    console.log($scope.scene);
                }
                $scope.titleText = getTitleText();
                $scope.saveText = getSaveText();
                $scope.showCropButton = false;
                $scope.modalScene = {};
                $scope.modalScene.previewImage = getScenePreviewImage();
                $scope.modalScene.name = getName();
                $scope.modalScene.selectedLights = getSelectedLights();
            }
        });

        $scope.$on('modal.removed', function (event, modal) {
            if (modal.id === "sceneModalImageCrop") {
                if (isCroppedImageAvailable()) {
                    $scope.showCropButton = true;
                    $scope.modalScene.previewImage = DataService.getSceneImageCropped();
                }
            }
        });

        var isCroppedImageAvailable = function () {
            var croppedImageUrl = DataService.getSceneImageCropped();
            return croppedImageUrl !== null;
        };

        var getSelectedLights = function () {
            if (!$scope.isEditMode) {
                return {};
            }
            var tmp = {};
            angular.forEach($scope.scene.lights, function (lightId) {
                tmp[lightId] = true;
            });
            return tmp;
        };

        var getName = function () {
            if ($scope.isEditMode) {
                return $scope.scene.name;
            }
            return "";
        };

        var getTitleText = function () {
            if ($scope.isEditMode) {
                return $scope.scene.name;
            } else {
                return $filter('translate')('Home_LightList_CreateScene');
            }
        };

        var getSaveText = function () {
            if ($scope.isEditMode) {
                return $filter('translate')('SINGLE_Update');
            } else {
                return $filter('translate')('SINGLE_Create');
            }
        };

        var getScenePreviewImage = function () {
            if ($scope.isEditMode) {
                if (angular.isDefined($scope.allImages) &&
                    angular.isDefined($scope.scene.image) &&
                    $scope.scene.image !== null &&
                    $scope.scene.image in $scope.allImages) {
                    return $scope.allImages[$scope.scene.image];
                }
            }
            return PlaceholderDataUrl;
        };

        $scope.closeModal = function () {
            $scope.sceneModal.hide();
        };

        $scope.deleteScene = function (sceneId) {
            console.log(sceneId);

            function onConfirm(buttonIndex) {
                if (buttonIndex === 1) //delete
                {
                    HueService.deleteScene(sceneId).then(function (data) {
                        DataService.removeCustomScene(sceneId);
                        $scope.refresh();
                        $scope.closeModal();
                    });
                }
            }

            navigator.notification.confirm(
                'Do you really want to delete the scene "' + $scope.scene.name + '"?', // message
                onConfirm, // callback to invoke with index of button pressed
                'Delete', // title
                    ['Delete', 'Cancel'] // buttonLabels
            );
        };

        $scope.createOrUpdateScene = function () {
            var tmp = [];

            angular.forEach($scope.modalScene.selectedLights, function (value, key) {
                if (value === true) {
                    tmp.push(key);
                }
            });
            if (tmp.length === 0) {
                navigator.notification.alert($filter('translate')('Select_At_Least_1_Light'), null, $filter('translate')('ERROR_TITLE'));
            } else if ($scope.modalScene.name.length === 0) {
                navigator.notification.alert($filter('translate')('Enter_Name'), null, $filter('translate')('ERROR_TITLE'));
            } else if ($scope.modalScene.name.length > 32) {
                navigator.notification.alert($filter('translate')('Name_Too_Long'), null, $filter('translate')('ERROR_TITLE'));
            }

            if (tmp.length > 0 && $scope.modalScene.name.length > 0 && $scope.modalScene.name.length <= 32) {
                if ($scope.isEditMode) {
                    HueService.modifyScene($scope.scene.id, $scope.modalScene.name, tmp).then(function (data) {
                        console.log(data);
                        DataService.removeCustomScene($scope.scene.id);
                        DataService.addCustomScene($scope.scene.id, $scope.modalScene.name, tmp, DataService.getSceneImageCropped());
                        $scope.closeModal();
                        if (angular.isDefined($scope.refresh)) {
                            $scope.refresh();
                        }
                        $state.go('main.home_tab.scenes');
                    });
                } else {
                    HueService.createScene($scope.modalScene.name, tmp).then(function (data) {
                        console.log(data);
                        DataService.addCustomScene(data[0].success.id, $scope.modalScene.name, tmp, DataService.getSceneImageCropped());
                        $scope.closeModal();
                        if (angular.isDefined($scope.refresh)) {
                            $scope.refresh();
                        }
                        $state.go('main.home_tab.scenes');
                    });
                }
            }
        };

        $scope.openSceneModalImageCrop = function () {
            $ionicModal.fromTemplateUrl('templates/home/modals/sceneModalImageCrop.html', {
                id: 'sceneModalImageCrop',
                scope: $scope,
                animation: 'slide-in-up'
            }).then(function (modal) {
                $scope.sceneModalImageCrop = modal;
                $scope.sceneModalImageCrop.show();
            });
        };

        $scope.scenePictureAlbum = function () {
            UtilityService.getPictureAlbumAsDataUrl().then(function (data) {
                DataService.setSceneImage(data);
                $scope.openSceneModalImageCrop();
            });
        };

        $scope.scenePictureCamera = function () {
            UtilityService.getPictureCameraAsDataUrl().then(function (data) {
                DataService.setSceneImage(data);
                $scope.openSceneModalImageCrop();
            });
        };
    }

    ctrl.$inject = ['HueService', '$scope', 'DataService', '$filter', 'PlaceholderDataUrl', '$ionicModal', 'UtilityService', '$state'];
    return ctrl;

});<|endoftext|>


<commit_before>/**
* Created by Mauriel on 3/9/2017.
*/

function rep_res_to_irods_user_zone_ajax_submit(res_id) {
    $.ajax({
        url: "/hsapi/_internal/" + res_id + "/rep-res-bag-to-irods-user-zone/",
        type: "POST",
        data: {},
        success: function(json) {
            if (json.success) {
                $("#rep-alert-success").show();
                $("#rep-status-success").text(json.success);
            }
            if (json.error) {
                $("#rep-alert-error").show();
                $("#rep-status-error").text(json.error);
            }
            $('#rep-resource-to-irods-dialog').modal('hide');
        },
        error: function(xhr, errmsg, err) {
            status_error = xhr.status + ": " + xhr.responseText + ". Error message: " + errmsg;
            console.log(status_error);
            $("#rep-status-error").text(status_error);
            $('#rep-resource-to-irods-dialog').modal('hide');
        }
    });
}

// These event bindings will work even for elements created dynamically
$(document).on('click', '.btn-unshare-resource', function () {
    var formID = $(this).closest("form").attr("id");
    unshare_resource_ajax_submit(formID);
});

$(document).on('click', '.btn-undo-share', function () {
    var formID = $(this).closest("form").attr("id");
    undo_share_ajax_submit(formID);
});

$(document).ready(function() {
    var resID = $("#resID").val();

    var btnDisabledTexts = {
        'delete': 'Deleting...',
        'add content': 'Adding Content...',
        'save changes': 'Saving Changes...',
    };

    function setButtonDisabledText(btn) {
        let buttonText = btn.text().toLowerCase().trim();
        btn.text(btnDisabledTexts[buttonText] !== null ? btnDisabledTexts[buttonText] : "Saving Changes...");
        btn.addClass("disabled");
    }

    $("#btn-replicate").click(function() {
        rep_res_to_irods_user_zone_ajax_submit(resID);
    });

    $("#download-bag-btn").click(function() {
        $("#license-agree-dialog-bag").modal('hide');
    });

    $("#btn-add-author, #btn-add-hydroshare-user").click(function () {
        $(this).text("Saving Changes...");
        $(this).addClass("disabled");
        var response = get_user_info_ajax_submit('/hsapi/_internal/get-user-or-group-data/', this);
        if (!response) {
            $(this).text("Save Changes");
            $(this).removeClass("disabled");
        }
    });

    // Disables the button after it has been clicked and its closest form was found to be valid
    $(".btn-disable-after-valid").click(function () {
        if ($(this).closest("form")[0].checkValidity()) {
            setButtonDisabledText($(this));
        }
    });

    // Disables the button after it is clicked
    $(".btn-disable-after").click(function () {
        setButtonDisabledText($(this));
    });

    $("#btn-confirm-extended-metadata").click(addEditExtraMeta2Table);
    $("#btn-confirm-delete-extended-metadata").click(removeExtraMetaTable);

    $("input#user-autocomplete").addClass("form-control");

    $('#usage-info').on('hidden.bs.collapse', function () {
        $("a[data-target='#usage-info']").text("Show More");
    }).on('shown.bs.collapse', function () {
        $("a[data-target='#usage-info']").text("Show Less");
    });

    $(".authors-wrapper").on("click", ".author-modal-trigger", function () {
        $("#edit-creator-dialog").modal('show');
        let data = $.extend(true, {}, $(this).data());    // Shallow copy
        let dialog = $("#edit-creator-dialog");
        let form = dialog.find("form");

        // The resource must have at least one author.
        if ($(".author-modal-trigger").length <= 1) {
            // Disable delete of author if only one exists
            dialog.find(".modal-body--delete").hide();
            $("#confirm-delete-author").find(".btn-danger").removeAttr("href");
        }
        else {
            dialog.find("modal-body--delete").show();
            $("#confirm-delete-author").find(".btn-danger").attr("href", "/hsapi/_internal/" + SHORT_ID + "/creator/" + data.id + "/delete-metadata/");
        }

        form.attr("action", "/hsapi/_internal/" + SHORT_ID + "/creator/" + data.id + "/update-metadata/");

        data.order -= 1;    // The value we use in the back end is 0 based and in the UI it is not

        // NAME
        let name = dialog.find(".control-group--name");
        name.find("input").attr("name", "creator-" + data.order + "-name");
        name.find("input").val(data.name != null ? data.name : "");
        if (!data.name) {
            name.hide();
            name.find("input").removeAttr("required");
        }
        else {
            name.show();
            name.find("input").attr("required", "required");
        }

        // USER IDENTIFIER
        let identifier = dialog.find(".control-group--description");
        identifier.find("input").val(data.description != null ? data.description : "");
        if (!data.description) {
            identifier.hide();
            identifier.find("input").removeAttr("name");
        }
        else {
            identifier.show();
            identifier.find("input").attr("name", "creator-" + data.order + "-description");
        }

        // ORGANIZATION
        let organization = dialog.find(".control-group--organization");
        organization.find("input").val(data.organization);
        organization.find("input").attr("name", "creator-" + data.order + "-organization");

        // If you don't have an author name, an organization is required
        if (!data.name) {
            organization.addClass("requiredField");
            organization.find("input").attr("required", "required");
            organization.find(".asteriskField").show();
        }
        else {
            organization.removeClass("requiredField");
            organization.find("input").removeAttr("required");
            organization.find(".asteriskField").hide();
        }

        // EMAIL
        let email = dialog.find(".control-group--email");
        email.find("input").attr("name", "creator-" + data.order + "-email");
        email.find("input").val(data.email != null ? data.email : "");

        // ADDRESS
        let address = dialog.find(".control-group--address");
        address.find("input").attr("name", "creator-" + data.order + "-address");
        address.find("input").val(data.address != null ? data.address : "");

        // PHONE
        let phone = dialog.find(".control-group--phone");
        phone.find("input").attr("name", "creator-" + data.order + "-phone");
        phone.find("input").val(data.phone != null ? data.phone : "");

        // HOMEPAGE
        let homepage = dialog.find(".control-group--homepage");
        homepage.find("input").attr("name", "creator-" + data.order + "-homepage");
        homepage.find("input").val(data.homepage != null ? data.homepage : "");

        // ORDER
        let order = dialog.find(".input-order");
        order.attr("name", "creator-" + data.order + "-order");
        order.val(data.order != null ? data.order : "");

        // IDENTIFIERS
        // const identifiers = ["googlescholarid", "orcid", "researchgateid", "researcerid"];
        const identifiersValueAttr = {
            googlescholarid: "GoogleScholarID",
            orcid: "ORCID",
            researchgateid: "ResearchGateID",
            researcerid: "ResearcherID"
        };

        dialog.find(".edit-identifiers-container .well:not(.identifier-template)").remove();

        for (let identifier in identifiersValueAttr) {
            if (data[identifier]) {
                var templateInstance = dialog.find(".identifier-template").clone();

                templateInstance.toggleClass("hidden", false);
                templateInstance.toggleClass("identifier-template", false);

                templateInstance.find(".select-identifier").attr("name", "identifier_name");
                templateInstance.find(".identifier-link-container input").attr("name", "identifier_link");

                templateInstance.find("select").val(identifiersValueAttr[identifier]);

                templateInstance.find("input[name='identifier_link']").val(data[identifier]);

                dialog.find(".edit-identifiers-container").append(templateInstance);
            }
        }
    });
});
<commit_msg>Prevent author order shifting bug Remove irrelevant comment<commit_after>/**
* Created by Mauriel on 3/9/2017.
*/

function rep_res_to_irods_user_zone_ajax_submit(res_id) {
    $.ajax({
        url: "/hsapi/_internal/" + res_id + "/rep-res-bag-to-irods-user-zone/",
        type: "POST",
        data: {},
        success: function(json) {
            if (json.success) {
                $("#rep-alert-success").show();
                $("#rep-status-success").text(json.success);
            }
            if (json.error) {
                $("#rep-alert-error").show();
                $("#rep-status-error").text(json.error);
            }
            $('#rep-resource-to-irods-dialog').modal('hide');
        },
        error: function(xhr, errmsg, err) {
            status_error = xhr.status + ": " + xhr.responseText + ". Error message: " + errmsg;
            console.log(status_error);
            $("#rep-status-error").text(status_error);
            $('#rep-resource-to-irods-dialog').modal('hide');
        }
    });
}

// These event bindings will work even for elements created dynamically
$(document).on('click', '.btn-unshare-resource', function () {
    var formID = $(this).closest("form").attr("id");
    unshare_resource_ajax_submit(formID);
});

$(document).on('click', '.btn-undo-share', function () {
    var formID = $(this).closest("form").attr("id");
    undo_share_ajax_submit(formID);
});

$(document).ready(function() {
    var resID = $("#resID").val();

    var btnDisabledTexts = {
        'delete': 'Deleting...',
        'add content': 'Adding Content...',
        'save changes': 'Saving Changes...',
    };

    function setButtonDisabledText(btn) {
        let buttonText = btn.text().toLowerCase().trim();
        btn.text(btnDisabledTexts[buttonText] !== null ? btnDisabledTexts[buttonText] : "Saving Changes...");
        btn.addClass("disabled");
    }

    $("#btn-replicate").click(function() {
        rep_res_to_irods_user_zone_ajax_submit(resID);
    });

    $("#download-bag-btn").click(function() {
        $("#license-agree-dialog-bag").modal('hide');
    });

    $("#btn-add-author, #btn-add-hydroshare-user").click(function () {
        $(this).text("Saving Changes...");
        $(this).addClass("disabled");
        var response = get_user_info_ajax_submit('/hsapi/_internal/get-user-or-group-data/', this);
        if (!response) {
            $(this).text("Save Changes");
            $(this).removeClass("disabled");
        }
    });

    // Disables the button after it has been clicked and its closest form was found to be valid
    $(".btn-disable-after-valid").click(function () {
        if ($(this).closest("form")[0].checkValidity()) {
            setButtonDisabledText($(this));
        }
    });

    // Disables the button after it is clicked
    $(".btn-disable-after").click(function () {
        setButtonDisabledText($(this));
    });

    $("#btn-confirm-extended-metadata").click(addEditExtraMeta2Table);
    $("#btn-confirm-delete-extended-metadata").click(removeExtraMetaTable);

    $("input#user-autocomplete").addClass("form-control");

    $('#usage-info').on('hidden.bs.collapse', function () {
        $("a[data-target='#usage-info']").text("Show More");
    }).on('shown.bs.collapse', function () {
        $("a[data-target='#usage-info']").text("Show Less");
    });

    $(".authors-wrapper").on("click", ".author-modal-trigger", function () {
        $("#edit-creator-dialog").modal('show');
        let data = $.extend(true, {}, $(this).data());    // Shallow copy
        let dialog = $("#edit-creator-dialog");
        let form = dialog.find("form");

        // The resource must have at least one author.
        if ($(".author-modal-trigger").length <= 1) {
            // Disable delete of author if only one exists
            dialog.find(".modal-body--delete").hide();
            $("#confirm-delete-author").find(".btn-danger").removeAttr("href");
        }
        else {
            dialog.find("modal-body--delete").show();
            $("#confirm-delete-author").find(".btn-danger").attr("href", "/hsapi/_internal/" + SHORT_ID + "/creator/" + data.id + "/delete-metadata/");
        }

        form.attr("action", "/hsapi/_internal/" + SHORT_ID + "/creator/" + data.id + "/update-metadata/");

        data.order -= 1;    // The value we use in the back end is 0 based and in the UI it is not

        // NAME
        let name = dialog.find(".control-group--name");
        name.find("input").attr("name", "creator-" + data.order + "-name");
        name.find("input").val(data.name != null ? data.name : "");
        if (!data.name) {
            name.hide();
            name.find("input").removeAttr("required");
        }
        else {
            name.show();
            name.find("input").attr("required", "required");
        }

        // USER IDENTIFIER
        let identifier = dialog.find(".control-group--description");
        identifier.find("input").val(data.description != null ? data.description : "");
        if (!data.description) {
            identifier.hide();
            identifier.find("input").removeAttr("name");
        }
        else {
            identifier.show();
            identifier.find("input").attr("name", "creator-" + data.order + "-description");
        }

        // ORGANIZATION
        let organization = dialog.find(".control-group--organization");
        organization.find("input").val(data.organization);
        organization.find("input").attr("name", "creator-" + data.order + "-organization");

        // If you don't have an author name, an organization is required
        if (!data.name) {
            organization.addClass("requiredField");
            organization.find("input").attr("required", "required");
            organization.find(".asteriskField").show();
        }
        else {
            organization.removeClass("requiredField");
            organization.find("input").removeAttr("required");
            organization.find(".asteriskField").hide();
        }

        // EMAIL
        let email = dialog.find(".control-group--email");
        email.find("input").attr("name", "creator-" + data.order + "-email");
        email.find("input").val(data.email != null ? data.email : "");

        // ADDRESS
        let address = dialog.find(".control-group--address");
        address.find("input").attr("name", "creator-" + data.order + "-address");
        address.find("input").val(data.address != null ? data.address : "");

        // PHONE
        let phone = dialog.find(".control-group--phone");
        phone.find("input").attr("name", "creator-" + data.order + "-phone");
        phone.find("input").val(data.phone != null ? data.phone : "");

        // HOMEPAGE
        let homepage = dialog.find(".control-group--homepage");
        homepage.find("input").attr("name", "creator-" + data.order + "-homepage");
        homepage.find("input").val(data.homepage != null ? data.homepage : "");

        // ORDER
        let order = dialog.find(".input-order");
        order.attr("name", "creator-" + data.order + "-order");
        order.val(data.order != null ? (parseInt(data.order) + 1) : "");

        // IDENTIFIERS
        const identifiersValueAttr = {
            googlescholarid: "GoogleScholarID",
            orcid: "ORCID",
            researchgateid: "ResearchGateID",
            researcerid: "ResearcherID"
        };

        dialog.find(".edit-identifiers-container .well:not(.identifier-template)").remove();

        for (let identifier in identifiersValueAttr) {
            if (data[identifier]) {
                var templateInstance = dialog.find(".identifier-template").clone();

                templateInstance.toggleClass("hidden", false);
                templateInstance.toggleClass("identifier-template", false);

                templateInstance.find(".select-identifier").attr("name", "identifier_name");
                templateInstance.find(".identifier-link-container input").attr("name", "identifier_link");

                templateInstance.find("select").val(identifiersValueAttr[identifier]);

                templateInstance.find("input[name='identifier_link']").val(data[identifier]);

                dialog.find(".edit-identifiers-container").append(templateInstance);
            }
        }
    });
});
<|endoftext|>


<commit_before>/*!
 * Sizzle CSS Selector Engine v1.10.20-pre
 * http://sizzlejs.com/
 *
 * Copyright 2013 jQuery Foundation, Inc. and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2014-04-21
 */

/*
 * Fizzle.js
 * Adapted from Sizzle.js
 */
var _ = require('_'),

    _cache         = require('../../../cache'),
    _tokenCache    = _cache(),
    _subqueryCache = _cache(),

    _error = (!console || !console.error) ? _.noop :
        function(selector) {
            console.error('Invalid query selector (caught): ' + selector);
        };

var _booleans = 'checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped',

    _fromCharCode = String.fromCharCode,

    // http://www.w3.org/TR/css3-selectors/#whitespace
    _whitespace = '[\\x20\\t\\r\\n\\f]',

    // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
    _identifier = '(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+',

    // NOTE: Leaving double quotes to reduce escaping
    // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
    _attributes = "\\[" + _whitespace + "*(" + _identifier + ")(?:" + _whitespace +
        // Operator (capture 2)
        "*([*^$|!~]?=)" + _whitespace +
        // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
        "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + _identifier + "))|)" + _whitespace +
        "*\\]",

    _pseudos = ":(" + _identifier + ")(?:\\((" +
        // To reduce the number of selectors needing _tokenize in the preFilter, prefer arguments:
        // 1. quoted (capture 3; capture 4 or capture 5)
        "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
        // 2. simple (capture 6)
        "((?:\\\\.|[^\\\\()[\\]]|" + _attributes + ")*)|" +
        // 3. anything else (capture 2)
        ".*" +
        ")\\)|)",

    _rcomma = new RegExp('^' + _whitespace + '*,' + _whitespace + '*'),
    _rcombinators = new RegExp('^' + _whitespace + '*([>+~]|' + _whitespace + ')' + _whitespace + '*'),

    _rpseudo = new RegExp(_pseudos),

    _matchExpr = {
        ID:     new RegExp('^#('   + _identifier + ')'),
        CLASS:  new RegExp('^\\.(' + _identifier + ')'),
        TAG:    new RegExp('^('    + _identifier + '|[*])'),
        ATTR:   new RegExp('^'     + _attributes),
        PSEUDO: new RegExp('^'     + _pseudos),
        CHILD:  new RegExp('^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(' + _whitespace +
            '*(even|odd|(([+-]|)(\\d*)n|)' + _whitespace + '*(?:([+-]|)' + _whitespace +
            '*(\\d+)|))' + _whitespace + '*\\)|)', 'i'),

        bool:   new RegExp('^(?:' + _booleans + ')$', 'i'),

        // For use in libraries implementing .is()
        // We use this for POS matching in `select`
        needsContext: new RegExp('^' + _whitespace + '*[>+~]|:(even|odd|nth|eq|gt|lt|first|last)(?:\\(' +
            _whitespace + '*((?:-\\d)?\\d*)' + _whitespace + '*\\)|)(?=[^-]|$)', 'i')
    },

    // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
    _runescape = new RegExp('\\\\([\\da-f]{1,6}' + _whitespace + '?|(' + _whitespace + ')|.)', 'ig'),
    _funescape = function(_, escaped, escapedWhitespace) {
        var high = '0x' + (escaped - 0x10000);
        // NaN means non-codepoint
        // Support: Firefox<24
        // Workaround erroneous numeric interpretation of +'0x'
        return high !== high || escapedWhitespace ?
            escaped :
            high < 0 ?
                // BMP codepoint
                _fromCharCode(high + 0x10000) :
                // Supplemental Plane codepoint (surrogate pair)
                _fromCharCode((high >> 10) | 0xD800, (high & 0x3FF) | 0xDC00);
    },

    _preFilter = {
        ATTR: function(match) {
            match[1] = match[1].replace(_runescape, _funescape);

            // Move the given value to match[3] whether quoted or unquoted
            match[3] = ( match[3] || match[4] || match[5] || '' ).replace(_runescape, _funescape);

            if (match[2] === '~=') {
                match[3] = ' ' + match[3] + ' ';
            }

            return match.slice(0, 4);
        },

        CHILD: function(match) {
            /* matches from _matchExpr['CHILD']
                1 type (only|nth|...)
                2 what (child|of-type)
                3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
                4 xn-component of xn+y argument ([+-]?\d*n|)
                5 sign of xn-component
                6 x of xn-component
                7 sign of y-component
                8 y of y-component
             */
            match[1] = match[1].toLowerCase();

            if (match[1].slice(0, 3) === 'nth') {
                // nth-* requires argument
                if (!match[3]) {
                    throw new Error(match[0]);
                }

                // numeric x and y parameters for Expr.filter.CHILD
                // remember that false/true cast respectively to 0/1
                match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === 'even' || match[3] === 'odd'));
                match[5] = +(( match[7] + match[8]) || match[3] === 'odd');

            // other types prohibit arguments
            } else if (match[3]) {
                throw new Error(match[0]);
            }

            return match;
        },

        PSEUDO: function(match) {
            var excess,
                unquoted = !match[6] && match[2];

            if (_matchExpr.CHILD.test(match[0])) {
                return null;
            }

            // Accept quoted arguments as-is
            if (match[3]) {
                match[2] = match[4] || match[5] || '';

            // Strip excess characters from unquoted arguments
            } else if (unquoted && _rpseudo.test(unquoted) &&
                // Get excess from _tokenize (recursively)
                (excess = _tokenize(unquoted, true)) &&
                // advance to the next closing parenthesis
                (excess = unquoted.indexOf(')', unquoted.length - excess) - unquoted.length)) {

                // excess is a negative index
                match[0] = match[0].slice(0, excess);
                match[2] = unquoted.slice(0, excess);
            }

            // Return only captures needed by the pseudo filter method (type and argument)
            return match.slice(0, 3);
        }
    };

/**
 * Splits the given comma-separated CSS selector into separate sub-queries.
 * @param  {String} selector Full CSS selector (e.g., 'a, input:focus, div[attr="value"]').
 * @param  {Boolean} [parseOnly=false]
 * @return {String[]|Number|unll} Array of sub-queries (e.g., [ 'a', 'input:focus', 'div[attr="(value1),[value2]"]') or null if there was an error parsing.
 * @private
 */
var _tokenize = function(selector, parseOnly) {
    var cached = _tokenCache.get(selector);

    if (cached) {
        return parseOnly ? 0 : cached.slice(0);
    }

    var /** @type {String} */
        type,

        /** @type {RegExp} */
        regex,

        /** @type {Array} */
        match,

        /** @type {String} */
        matched,

        /** @type {String[]} */
        subqueries = [],

        /** @type {String} */
        subquery = '',

        /** @type {String} */
        soFar = selector;

    while (soFar) {
        // Comma and first run
        if (!matched || (match = _rcomma.exec(soFar))) {
            if (match) {
                // Don't consume trailing commas as valid
                soFar = soFar.slice(match[0].length) || soFar;
            }
            if (subquery) { subqueries.push(subquery); }
            subquery = '';
        }

        matched = null;

        // Combinators
        if ((match = _rcombinators.exec(soFar))) {
            matched = match.shift();
            subquery += matched;
            soFar = soFar.slice(matched.length);
        }

        // Filters
        for (type in _matchExpr) {
            regex = _matchExpr[type];
            match = regex.exec(soFar);

            if (match && (!_preFilter[type] || (match = _preFilter[type](match)))) {
                matched = match.shift();
                subquery += matched;
                soFar = soFar.slice(matched.length);

                break;
            }
        }

        if (!matched) {
            break;
        }
    }

    if (subquery) { subqueries.push(subquery); }

    // Return the length of the invalid excess
    // if we're just parsing.
    if (parseOnly) { return soFar.length; }

    if (soFar) { _error(selector); return null; }

    return _tokenCache.set(selector, subqueries).slice();
};

module.exports = {
    /**
     * Splits the given comma-separated CSS selector into separate sub-queries.
     * @param  {String} selector Full CSS selector (e.g., 'a, input:focus, div[attr="value"]').
     * @return {String[]} Array of sub-queries (e.g., [ 'a', 'input:focus', 'div[attr="(value1),[value2]"]').
     */
    subqueries: function(selector) {
        return _subqueryCache.getOrSet(selector, function() {
            return _tokenize(selector);
        });
    }
};
<commit_msg>Change quoting characters to avoid parsing weirdness<commit_after>/*
 * Fizzle.js
 * Adapted from Sizzle.js
 */
var _ = require('_'),

    _cache         = require('../../../cache'),
    _tokenCache    = _cache(),
    _subqueryCache = _cache(),

    _error = (!console || !console.error) ? _.noop :
        function(selector) {
            console.error('Invalid query selector (caught): ' + selector);
        };

var _booleans = 'checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped',

    _fromCharCode = String.fromCharCode,

    // http://www.w3.org/TR/css3-selectors/#whitespace
    _whitespace = '[\\x20\\t\\r\\n\\f]',

    // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
    _identifier = '(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+',

    // NOTE: Leaving double quotes to reduce escaping
    // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
    _attributes = "\\[" + _whitespace + "*(" + _identifier + ")(?:" + _whitespace +
        // Operator (capture 2)
        "*([*^$|!~]?=)" + _whitespace +
        // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
        "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + _identifier + "))|)" + _whitespace +
        "*\\]",

    _pseudos = ":(" + _identifier + ")(?:\\((" +
        // To reduce the number of selectors needing _tokenize in the preFilter, prefer arguments:
        // 1. quoted (capture 3; capture 4 or capture 5)
        "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
        // 2. simple (capture 6)
        "((?:\\\\.|[^\\\\()[\\]]|" + _attributes + ")*)|" +
        // 3. anything else (capture 2)
        ".*" +
        ")\\)|)",

    _rcomma = new RegExp('^' + _whitespace + '*,' + _whitespace + '*'),
    _rcombinators = new RegExp('^' + _whitespace + '*([>+~]|' + _whitespace + ')' + _whitespace + '*'),

    _rpseudo = new RegExp(_pseudos),

    _matchExpr = {
        ID:     new RegExp('^#('   + _identifier + ')'),
        CLASS:  new RegExp('^\\.(' + _identifier + ')'),
        TAG:    new RegExp('^('    + _identifier + '|[*])'),
        ATTR:   new RegExp('^'     + _attributes),
        PSEUDO: new RegExp('^'     + _pseudos),
        CHILD:  new RegExp('^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(' + _whitespace +
            '*(even|odd|(([+-]|)(\\d*)n|)' + _whitespace + '*(?:([+-]|)' + _whitespace +
            '*(\\d+)|))' + _whitespace + '*\\)|)', 'i'),

        // Note: Using double quotes due to parsing weirdness
        bool:   new RegExp("^(?:" + _booleans + ")$", "i"),

        // For use in libraries implementing .is()
        // We use this for POS matching in `select`
        needsContext: new RegExp('^' + _whitespace + '*[>+~]|:(even|odd|nth|eq|gt|lt|first|last)(?:\\(' +
            _whitespace + '*((?:-\\d)?\\d*)' + _whitespace + '*\\)|)(?=[^-]|$)', 'i')
    },

    // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
    _runescape = new RegExp('\\\\([\\da-f]{1,6}' + _whitespace + '?|(' + _whitespace + ')|.)', 'ig'),
    _funescape = function(_, escaped, escapedWhitespace) {
        var high = '0x' + (escaped - 0x10000);
        // NaN means non-codepoint
        // Support: Firefox<24
        // Workaround erroneous numeric interpretation of +'0x'
        return high !== high || escapedWhitespace ?
            escaped :
            high < 0 ?
                // BMP codepoint
                _fromCharCode(high + 0x10000) :
                // Supplemental Plane codepoint (surrogate pair)
                _fromCharCode((high >> 10) | 0xD800, (high & 0x3FF) | 0xDC00);
    },

    _preFilter = {
        ATTR: function(match) {
            match[1] = match[1].replace(_runescape, _funescape);

            // Move the given value to match[3] whether quoted or unquoted
            match[3] = ( match[3] || match[4] || match[5] || '' ).replace(_runescape, _funescape);

            if (match[2] === '~=') {
                match[3] = ' ' + match[3] + ' ';
            }

            return match.slice(0, 4);
        },

        CHILD: function(match) {
            /* matches from _matchExpr['CHILD']
                1 type (only|nth|...)
                2 what (child|of-type)
                3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
                4 xn-component of xn+y argument ([+-]?\d*n|)
                5 sign of xn-component
                6 x of xn-component
                7 sign of y-component
                8 y of y-component
             */
            match[1] = match[1].toLowerCase();

            if (match[1].slice(0, 3) === 'nth') {
                // nth-* requires argument
                if (!match[3]) {
                    throw new Error(match[0]);
                }

                // numeric x and y parameters for Expr.filter.CHILD
                // remember that false/true cast respectively to 0/1
                match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === 'even' || match[3] === 'odd'));
                match[5] = +(( match[7] + match[8]) || match[3] === 'odd');

            // other types prohibit arguments
            } else if (match[3]) {
                throw new Error(match[0]);
            }

            return match;
        },

        PSEUDO: function(match) {
            var excess,
                unquoted = !match[6] && match[2];

            if (_matchExpr.CHILD.test(match[0])) {
                return null;
            }

            // Accept quoted arguments as-is
            if (match[3]) {
                match[2] = match[4] || match[5] || '';

            // Strip excess characters from unquoted arguments
            } else if (unquoted && _rpseudo.test(unquoted) &&
                // Get excess from _tokenize (recursively)
                (excess = _tokenize(unquoted, true)) &&
                // advance to the next closing parenthesis
                (excess = unquoted.indexOf(')', unquoted.length - excess) - unquoted.length)) {

                // excess is a negative index
                match[0] = match[0].slice(0, excess);
                match[2] = unquoted.slice(0, excess);
            }

            // Return only captures needed by the pseudo filter method (type and argument)
            return match.slice(0, 3);
        }
    };

/**
 * Splits the given comma-separated CSS selector into separate sub-queries.
 * @param  {String} selector Full CSS selector (e.g., 'a, input:focus, div[attr="value"]').
 * @param  {Boolean} [parseOnly=false]
 * @return {String[]|Number|unll} Array of sub-queries (e.g., [ 'a', 'input:focus', 'div[attr="(value1),[value2]"]') or null if there was an error parsing.
 * @private
 */
var _tokenize = function(selector, parseOnly) {
    var cached = _tokenCache.get(selector);

    if (cached) {
        return parseOnly ? 0 : cached.slice(0);
    }

    var /** @type {String} */
        type,

        /** @type {RegExp} */
        regex,

        /** @type {Array} */
        match,

        /** @type {String} */
        matched,

        /** @type {String[]} */
        subqueries = [],

        /** @type {String} */
        subquery = '',

        /** @type {String} */
        soFar = selector;

    while (soFar) {
        // Comma and first run
        if (!matched || (match = _rcomma.exec(soFar))) {
            if (match) {
                // Don't consume trailing commas as valid
                soFar = soFar.slice(match[0].length) || soFar;
            }
            if (subquery) { subqueries.push(subquery); }
            subquery = '';
        }

        matched = null;

        // Combinators
        if ((match = _rcombinators.exec(soFar))) {
            matched = match.shift();
            subquery += matched;
            soFar = soFar.slice(matched.length);
        }

        // Filters
        for (type in _matchExpr) {
            regex = _matchExpr[type];
            match = regex.exec(soFar);

            if (match && (!_preFilter[type] || (match = _preFilter[type](match)))) {
                matched = match.shift();
                subquery += matched;
                soFar = soFar.slice(matched.length);

                break;
            }
        }

        if (!matched) {
            break;
        }
    }

    if (subquery) { subqueries.push(subquery); }

    // Return the length of the invalid excess
    // if we're just parsing.
    if (parseOnly) { return soFar.length; }

    if (soFar) { _error(selector); return null; }

    return _tokenCache.set(selector, subqueries).slice();
};

module.exports = {
    /**
     * Splits the given comma-separated CSS selector into separate sub-queries.
     * @param  {String} selector Full CSS selector (e.g., 'a, input:focus, div[attr="value"]').
     * @return {String[]} Array of sub-queries (e.g., [ 'a', 'input:focus', 'div[attr="(value1),[value2]"]').
     */
    subqueries: function(selector) {
        return _subqueryCache.getOrSet(selector, function() {
            return _tokenize(selector);
        });
    }
};
<|endoftext|>