<commit_before>/**
  * LzKeyboardKernel.js
  *
  * @copyright Copyright 2007-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic DHTML
  * @author Max Carlson <max@openlaszlo.org>
  */

// Receives keyboard events from the runtime
var LzKeyboardKernel = {
    __downKeysHash: {alt: false, control: false, shift: false, meta: false}
    ,__keyCodes: {}
    ,__keyboardEvent: function ( e ){
        if (!e) e = window.event;
        var delta = {};
        var dirty = false;
        var k = e['keyCode'];
        var t = e.type;
        var dh = LzKeyboardKernel.__downKeysHash;
        // TODO: really, all control characters should be skipped...
        // skip shift, ctrl, option keys to prevent duplicate sending - see LPP-4267
        if (k >= 0 && k != 16 && k != 17 && k != 18 && k != 224) {
            // TODO: add mapping to flash character codes?
            var s = String.fromCharCode(k).toLowerCase();
            LzKeyboardKernel.__keyCodes[s] = k;
            if (t == 'keyup') {
                if (dh[s] != false) {
                    delta[s] = false;
                    dirty = true;
                }
                dh[s] = false;
            } else if (t == 'keydown') {
                if (dh[s] != true) {
                    delta[s] = true;
                    dirty = true;
                }
                dh[s] = true;
            }
        }

        if (LzKeyboardKernel.__updateControlKeys(e, delta)) {
            dirty = true;
        }

        var stuck;
        var meta = e['metaKey'];
        if (LzSprite.prototype.quirks['detectstuckkeys']) {
            // see LPP-8210
            if (dh['meta'] != meta) {
                // look for stuck keys
                delta['control'] = meta;
                dirty = true;
                if (! meta) {
                    for (var key in dh) {
                        if (key == 'control' || key == 'shift' || key == 'alt' || key == 'meta') continue;
                        stuck = key;
                        delete dh[key];
                    }
                }
            }
        }
        dh['meta'] = meta;

        if (dirty) {
            var scope = LzKeyboardKernel.__scope;
            var callback = LzKeyboardKernel.__callback;
            if (scope && scope[callback]) {
                //console.log(t, s, k, delta, e.metaKey, e.ctrlKey, dh);
                if (stuck) {
                    // console.log('stuck key', key, keycode);
                    var keycode = LzKeyboardKernel.__keyCodes[stuck];
                    var fakedelta = {};
                    // FIXME: [20090602 anba] 'key' seems to be a typo, should it be 'stuck'?
                    fakedelta[key] = false;
                    scope[callback](fakedelta, keycode, 'onkeyup');
                }

                scope[callback](delta, k, 'on' + t);
            }
        }

        // cancel bubbling
        if (k >= 0) {
            if (k == 9) {
                //Debug.write('canceling tab');
                e.cancelBubble = true;
                e.returnValue = false;
                return false;
            } else if (LzKeyboardKernel.__cancelKeys && (k == 13 || k == 0 || k == 37 || k == 38 || k == 39 || k == 40) ) {
                //Debug.write('canceling key', k, t);
                // cancel event bubbling for enter, space(scroll) and arrow keys
                e.cancelBubble = true;
                e.returnValue = false;
                return false;
            }
        }
        //Debug.write('downKeysHash', t, k, dh, delta);
    }
    ,__updateControlKeys: function (e, delta) {
        var dh = LzKeyboardKernel.__downKeysHash;
        var dirty = false;
        if (delta) {
            var send = false;
        } else {
            // Called with mouse-event, see LzSprite, LzMouseKernel
            delta = {};
            var send = true;
        }
        var alt = e['altKey'];
        if (dh['alt'] != alt) {
            delta['alt'] = alt;
            dirty = true;
            if (LzSprite.prototype.quirks['alt_key_sends_control']) {
                delta['control'] = delta['alt'];
            }
        }
        var ctrl = e['ctrlKey'];
        if (dh['control'] != ctrl) {
            delta['control'] = ctrl;
            dirty = true;
        }
        var shift = e['shiftKey'];
        if (dh['shift'] != shift) {
            delta['shift'] = shift;
            dirty = true;
        }

        dh['alt'] = alt;
        dh['control'] = ctrl;
        dh['shift'] = shift;

        if (send && dirty) {
            var scope = LzKeyboardKernel.__scope;
            var callback = LzKeyboardKernel.__callback;
            if (scope && scope[callback]) {
                scope[callback](delta, 0, 'on' + e.type);
            }
        }

        return dirty;
    }
    ,__callback: null
    ,__scope: null
    ,__cancelKeys: true
    ,__lockFocus: null
    ,setCallback: function (scope, keyboardcallback) {
        this.__scope = scope;
        this.__callback = keyboardcallback;
    }
    ,setKeyboardControl: function (dhtmlKeyboardControl, force) {
        if (! force && LzKeyboardKernel.__lockFocus) {
            dhtmlKeyboardControl = true;
        }
        var handler = null;
        var setcontrol = (lz && lz.embed && lz.embed.options && lz.embed.options.cancelkeyboardcontrol != true) || true;
        if (setcontrol && dhtmlKeyboardControl) {
            //console.log('setKeyboardControl' + dhtmlKeyboardControl);
            handler = LzKeyboardKernel.__keyboardEvent;
        }
        var lzinputproto = LzInputTextSprite.prototype;
        if (lzinputproto.__focusedSprite) {
            // hide any focused inputtexts
            lzinputproto.__hideIfNotFocused();
        }
        // can't use lz.embed.attachEventHandler because we need to cancel events selectively
        if (LzSprite.prototype.quirks.keyboardlistentotop) {
            var doc = window.top.document;
        } else {
            var doc = document;
        }
        doc.onkeydown = handler;
        doc.onkeyup = handler;
        doc.onkeypress = handler;
    }
    // Called by lz.Keys when the last focusable element was reached.
    ,gotLastFocus: function () {
        //console.log('gotLastFocus', canvas.sprite.__LZdiv.mouseisover);
        if (! canvas.sprite.__LZdiv.mouseisover) LzKeyboardKernel.setKeyboardControl(false);
    }
    // Called to turn on/off restriction of focus to this application
    ,setGlobalFocusTrap: function (istrapped) {
        LzKeyboardKernel.__lockFocus = istrapped;
        if (LzSprite.prototype.quirks.activate_on_mouseover) {
            if (istrapped) {
                LzSprite.__rootSprite.__LZdiv.onmouseover();
            } else {
                LzSprite.__rootSprite.__LZdiv.onmouseout();
            }
        }
    }
}
<commit_msg>Change 20090602-bargull-fI3 by bargull@dell--p4--2-53 on 2009-06-02 21:58:33     in /home/Admin/src/svn/openlaszlo/trunk     for http://svn.openlaszlo.org/openlaszlo/trunk<commit_after>/**
  * LzKeyboardKernel.js
  *
  * @copyright Copyright 2007-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic DHTML
  * @author Max Carlson <max@openlaszlo.org>
  */

// Receives keyboard events from the runtime
var LzKeyboardKernel = {
    __downKeysHash: {alt: false, control: false, shift: false, meta: false}
    ,__keyCodes: {}
    ,__keyboardEvent: function ( e ){
        if (!e) e = window.event;
        var delta = {};
        var dirty = false;
        var k = e['keyCode'];
        var t = e.type;
        var dh = LzKeyboardKernel.__downKeysHash;
        // TODO: really, all control characters should be skipped...
        // skip shift, ctrl, option keys to prevent duplicate sending - see LPP-4267
        if (k >= 0 && k != 16 && k != 17 && k != 18 && k != 224) {
            // TODO: add mapping to flash character codes?
            var s = String.fromCharCode(k).toLowerCase();
            LzKeyboardKernel.__keyCodes[s] = k;
            if (t == 'keyup') {
                if (dh[s] != false) {
                    delta[s] = false;
                    dirty = true;
                }
                dh[s] = false;
            } else if (t == 'keydown') {
                if (dh[s] != true) {
                    delta[s] = true;
                    dirty = true;
                }
                dh[s] = true;
            }
        }

        if (LzKeyboardKernel.__updateControlKeys(e, delta)) {
            dirty = true;
        }

        if (dirty) {
            var scope = LzKeyboardKernel.__scope;
            var callback = LzKeyboardKernel.__callback;
            if (scope && scope[callback]) {
                scope[callback](delta, k, 'on' + t);
            }
        }

        // cancel bubbling
        if (k >= 0) {
            if (k == 9) {
                //Debug.write('canceling tab');
                e.cancelBubble = true;
                e.returnValue = false;
                return false;
            } else if (LzKeyboardKernel.__cancelKeys && (k == 13 || k == 0 || k == 37 || k == 38 || k == 39 || k == 40) ) {
                //Debug.write('canceling key', k, t);
                // cancel event bubbling for enter, space(scroll) and arrow keys
                e.cancelBubble = true;
                e.returnValue = false;
                return false;
            }
        }
        //Debug.write('downKeysHash', t, k, dh, delta);
    }
    ,__updateControlKeys: function (e, delta) {
        var quirks = LzSprite.prototype.quirks;
        var dh = LzKeyboardKernel.__downKeysHash;
        var dirty = false;
        if (delta) {
            var send = false;
        } else {
            // Called with mouse-event, see LzSprite, LzMouseKernel
            delta = {};
            var send = true;
        }
        var alt = e['altKey'];
        if (dh['alt'] != alt) {
            delta['alt'] = alt;
            dirty = true;
            if (quirks['alt_key_sends_control']) {
                delta['control'] = delta['alt'];
            }
        }
        var ctrl = e['ctrlKey'];
        if (dh['control'] != ctrl) {
            delta['control'] = ctrl;
            dirty = true;
        }
        var shift = e['shiftKey'];
        if (dh['shift'] != shift) {
            delta['shift'] = shift;
            dirty = true;
        }
        var stuck;
        var meta = e['metaKey'];
        if (quirks['detectstuckkeys']) {
            // see LPP-8210
            if (dh['meta'] != meta) {
                // look for stuck keys
                delta['control'] = meta;
                dirty = true;
                if (! meta) {
                    for (var key in dh) {
                        if (key == 'control' || key == 'shift' || key == 'alt' || key == 'meta') continue;
                        stuck = key;
                        delete dh[key];
                    }
                }
            }
        }

        dh['alt'] = alt;
        dh['control'] = ctrl;
        dh['shift'] = shift;
        dh['meta'] = meta;

        if (dirty && (send || stuck)) {
            var scope = LzKeyboardKernel.__scope;
            var callback = LzKeyboardKernel.__callback;
            if (scope && scope[callback]) {
                //console.log(t, s, k, delta, e.metaKey, e.ctrlKey, dh);
                if (stuck) {
                    // console.log('stuck key', key, keycode);
                    var keycode = LzKeyboardKernel.__keyCodes[stuck];
                    var fakedelta = {};
                    // FIXME: [20090602 anba] 'key' seems to be a typo, should it be 'stuck'?
                    fakedelta[key] = false;
                    scope[callback](fakedelta, keycode, 'onkeyup');
                }

                if (send) {
                    scope[callback](delta, 0, 'on' + e.type);
                }
            }
        }

        return dirty;
    }
    ,__callback: null
    ,__scope: null
    ,__cancelKeys: true
    ,__lockFocus: null
    ,setCallback: function (scope, keyboardcallback) {
        this.__scope = scope;
        this.__callback = keyboardcallback;
    }
    ,setKeyboardControl: function (dhtmlKeyboardControl, force) {
        if (! force && LzKeyboardKernel.__lockFocus) {
            dhtmlKeyboardControl = true;
        }
        var handler = null;
        var setcontrol = (lz && lz.embed && lz.embed.options && lz.embed.options.cancelkeyboardcontrol != true) || true;
        if (setcontrol && dhtmlKeyboardControl) {
            //console.log('setKeyboardControl' + dhtmlKeyboardControl);
            handler = LzKeyboardKernel.__keyboardEvent;
        }
        var lzinputproto = LzInputTextSprite.prototype;
        if (lzinputproto.__focusedSprite) {
            // hide any focused inputtexts
            lzinputproto.__hideIfNotFocused();
        }
        // can't use lz.embed.attachEventHandler because we need to cancel events selectively
        if (LzSprite.prototype.quirks.keyboardlistentotop) {
            var doc = window.top.document;
        } else {
            var doc = document;
        }
        doc.onkeydown = handler;
        doc.onkeyup = handler;
        doc.onkeypress = handler;
    }
    // Called by lz.Keys when the last focusable element was reached.
    ,gotLastFocus: function () {
        //console.log('gotLastFocus', canvas.sprite.__LZdiv.mouseisover);
        if (! canvas.sprite.__LZdiv.mouseisover) LzKeyboardKernel.setKeyboardControl(false);
    }
    // Called to turn on/off restriction of focus to this application
    ,setGlobalFocusTrap: function (istrapped) {
        LzKeyboardKernel.__lockFocus = istrapped;
        if (LzSprite.prototype.quirks.activate_on_mouseover) {
            if (istrapped) {
                LzSprite.__rootSprite.__LZdiv.onmouseover();
            } else {
                LzSprite.__rootSprite.__LZdiv.onmouseout();
            }
        }
    }
}
<|endoftext|>


<commit_before>import { JSZip } from 'meteor/silentcicero:jszip';
import { OHIF } from 'meteor/ohif:core';

const getNumberOfFilesToExport = function(studiesToExport) {
    let numberOfFilesToExport = 0;

    studiesToExport.forEach(study => {
        numberOfFilesToExport += getNumberOfFilesInStudy(study);
    });

    return numberOfFilesToExport;
}

const convertSizeToString = size => {
    const measuments = ['B', 'KB', 'MB', 'GB'];
    let totalBytes = size,
        measumentIndex = 0;

    while(totalBytes > 1024) {
        totalBytes /= 1024;
        measumentIndex++;
    }

    return `${totalBytes.toFixed(2)} ${measuments[measumentIndex]}`;
}

/**
 * Exports requested studies
 * @param studiesToExport Studies to export
 */
OHIF.studylist.exportStudies = studiesToExport => {
    if (studiesToExport.length < 1) {
        return;
    }

    queryStudiesWithProgress(studiesToExport)
    .then(exportQueriedStudiesWithProgress);
};

const exportQueriedStudiesWithProgress = studiesToExport => {
    const exportFilesCount = getNumberOfFilesToExport(studiesToExport);
    let exportHandler;

    return OHIF.ui.showFormDialog('dialogProgress', {
        title: 'Exporting Studies...',
        message: `Exported files: 0 / exportFilesCount`,
        total: getNumberOfFilesToExport(studiesToExport),
        task: {
            run: (dialog) => {
                exportHandler = exportQueriedStudies(studiesToExport, {
                    notify: stats => {
                        const fileSize = convertSizeToString(stats.totalBytes);

                        dialog.update(stats.processed);
                        dialog.setMessage(`Exported files: ${stats.processed} / ${stats.total} (${fileSize})`);
                    }
                });

                exportHandler.promise.then(() => {
                    dialog.done();
                }, () => {
                    dialog.setMessage('Failed to export studies');
                });
            }
        }
    }).then(null, err => {
        exportHandler.cancel();
    });
};

const exportQueriedStudies = (studiesToExport, options) => {
    const zip = new JSZip(),
          promises = [],
          pendingDownloads = [],
          exportFilesCount = getNumberOfFilesToExport(studiesToExport),
          notify = (options || {}).notify || function() { /* noop */ };

    const cancelDownloads = () => {
        while(pendingDownloads.length) {
            const download = pendingDownloads.pop();
            download.cancel();
        }
    };

    let totalBytes = 0;

    studiesToExport.forEach(study => {
        study.seriesList.forEach(series => {
            series.instances.forEach(instance => {
                if (!instance.wadouri) {
                    return;
                }

                const download = downloadDicomFile(instance);
                pendingDownloads.push(download);

                const promise = download.promise
                .then(data => {
                    const downloadIndex = pendingDownloads.indexOf(download);

                    totalBytes += data && data.size ? data.size : 0;

                    if(downloadIndex > -1) {
                        pendingDownloads.splice(downloadIndex, 1);
                    }

                    notify({
                        total: exportFilesCount,
                        processed: exportFilesCount - pendingDownloads.length,
                        totalBytes: totalBytes
                    })

                    return zipInstance(study, series, instance, zip, data)
                })
                .catch(err => {
                    if(!(err instanceof ExportStudyDownloadCanceledError)) {
                        OHIF.log.error('Failed to export studies', err);
                    }

                    cancelDownloads();
                    throw err;
                });

                promises.push(promise);
            });
        });
    });

    return {
        cancel: cancelDownloads,
        promise: Promise.all(promises).then(() => {
            const zipContent = zip.generate({ type: 'blob' });
            saveAs(zipContent, 'studies.zip');
        })
    }
};

const downloadDicomFile = instance => {
    let xhr,
        promiseReject;

    const promise = new Promise((resolve, reject) => {
        promiseReject = reject;

        xhr = new XMLHttpRequest();
        xhr.open('GET', instance.wadouri, true);
        xhr.responseType = 'blob';

        xhr.onload = () => {
            if (xhr.readyState === 4 && xhr.status !== 200) {
                return reject(new Error(`File not downloaded: ${instance.wadouri}`));
            }

            resolve(xhr.response)
        };

        xhr.onerror = () => {
            reject(new Error(`File not downloaded: ${instance.wadouri}`));
        };

        xhr.send();
    });

    return {
        promise: promise,
        cancel: () => {
            xhr.abort();
            promiseReject(new ExportStudyDownloadCanceledError('Download canceled'));
        }
    }
};

const zipInstance = (study, series, instance, zip, data) => {
    const fileReader = new FileReader(),
          blobFile = new Blob([data], { type: 'application/dicom' }),
          zipFolder = zip.folder(study.studyInstanceUid).folder(series.seriesInstanceUid);

    const promise = new Promise((resolve, reject) => {
        fileReader.onload = () => {
            try {
                zipFolder.file(`${instance.sopInstanceUid}.dcm`, fileReader.result, { binary: true });
                resolve()
            } catch(err) {
                reject(err);
            }
        };
    });

    fileReader.readAsArrayBuffer(blobFile);

    return promise;
};

const ExportStudyDownloadCanceledError = (message) => {
  this.name = 'ExportStudyDownloadCanceledError';
  this.message = message || 'Download canceled';
  this.stack = (new Error()).stack;
}
ExportStudyDownloadCanceledError.prototype = Object.create(Error.prototype);
ExportStudyDownloadCanceledError.prototype.constructor = ExportStudyDownloadCanceledError;
<commit_msg>Export studies :: Wrong message<commit_after>import { JSZip } from 'meteor/silentcicero:jszip';
import { OHIF } from 'meteor/ohif:core';

const getNumberOfFilesToExport = function(studiesToExport) {
    let numberOfFilesToExport = 0;

    studiesToExport.forEach(study => {
        numberOfFilesToExport += getNumberOfFilesInStudy(study);
    });

    return numberOfFilesToExport;
}

const convertSizeToString = size => {
    const measuments = ['B', 'KB', 'MB', 'GB'];
    let totalBytes = size,
        measumentIndex = 0;

    while(totalBytes > 1024) {
        totalBytes /= 1024;
        measumentIndex++;
    }

    return `${totalBytes.toFixed(2)} ${measuments[measumentIndex]}`;
}

/**
 * Exports requested studies
 * @param studiesToExport Studies to export
 */
OHIF.studylist.exportStudies = studiesToExport => {
    if (studiesToExport.length < 1) {
        return;
    }

    queryStudiesWithProgress(studiesToExport)
    .then(exportQueriedStudiesWithProgress);
};

const exportQueriedStudiesWithProgress = studiesToExport => {
    const exportFilesCount = getNumberOfFilesToExport(studiesToExport);
    let exportHandler;

    return OHIF.ui.showFormDialog('dialogProgress', {
        title: 'Exporting Studies...',
        message: `Exported files: 0 / ${exportFilesCount}`,
        total: getNumberOfFilesToExport(studiesToExport),
        task: {
            run: (dialog) => {
                exportHandler = exportQueriedStudies(studiesToExport, {
                    notify: stats => {
                        const fileSize = convertSizeToString(stats.totalBytes);

                        dialog.update(stats.processed);
                        dialog.setMessage(`Exported files: ${stats.processed} / ${stats.total} (${fileSize})`);
                    }
                });

                exportHandler.promise.then(() => {
                    dialog.done();
                }, () => {
                    dialog.setMessage('Failed to export studies');
                });
            }
        }
    }).then(null, err => {
        exportHandler.cancel();
    });
};

const exportQueriedStudies = (studiesToExport, options) => {
    const zip = new JSZip(),
          promises = [],
          pendingDownloads = [],
          exportFilesCount = getNumberOfFilesToExport(studiesToExport),
          notify = (options || {}).notify || function() { /* noop */ };

    const cancelDownloads = () => {
        while(pendingDownloads.length) {
            const download = pendingDownloads.pop();
            download.cancel();
        }
    };

    let totalBytes = 0;

    studiesToExport.forEach(study => {
        study.seriesList.forEach(series => {
            series.instances.forEach(instance => {
                if (!instance.wadouri) {
                    return;
                }

                const download = downloadDicomFile(instance);
                pendingDownloads.push(download);

                const promise = download.promise
                .then(data => {
                    const downloadIndex = pendingDownloads.indexOf(download);

                    totalBytes += data && data.size ? data.size : 0;

                    if(downloadIndex > -1) {
                        pendingDownloads.splice(downloadIndex, 1);
                    }

                    notify({
                        total: exportFilesCount,
                        processed: exportFilesCount - pendingDownloads.length,
                        totalBytes: totalBytes
                    })

                    return zipInstance(study, series, instance, zip, data)
                })
                .catch(err => {
                    if(!(err instanceof ExportStudyDownloadCanceledError)) {
                        OHIF.log.error('Failed to export studies', err);
                    }

                    cancelDownloads();
                    throw err;
                });

                promises.push(promise);
            });
        });
    });

    return {
        cancel: cancelDownloads,
        promise: Promise.all(promises).then(() => {
            const zipContent = zip.generate({ type: 'blob' });
            saveAs(zipContent, 'studies.zip');
        })
    }
};

const downloadDicomFile = instance => {
    let xhr,
        promiseReject;

    const promise = new Promise((resolve, reject) => {
        promiseReject = reject;

        xhr = new XMLHttpRequest();
        xhr.open('GET', instance.wadouri, true);
        xhr.responseType = 'blob';

        xhr.onload = () => {
            if (xhr.readyState === 4 && xhr.status !== 200) {
                return reject(new Error(`File not downloaded: ${instance.wadouri}`));
            }

            resolve(xhr.response)
        };

        xhr.onerror = () => {
            reject(new Error(`File not downloaded: ${instance.wadouri}`));
        };

        xhr.send();
    });

    return {
        promise: promise,
        cancel: () => {
            xhr.abort();
            promiseReject(new ExportStudyDownloadCanceledError('Download canceled'));
        }
    }
};

const zipInstance = (study, series, instance, zip, data) => {
    const fileReader = new FileReader(),
          blobFile = new Blob([data], { type: 'application/dicom' }),
          zipFolder = zip.folder(study.studyInstanceUid).folder(series.seriesInstanceUid);

    const promise = new Promise((resolve, reject) => {
        fileReader.onload = () => {
            try {
                zipFolder.file(`${instance.sopInstanceUid}.dcm`, fileReader.result, { binary: true });
                resolve()
            } catch(err) {
                reject(err);
            }
        };
    });

    fileReader.readAsArrayBuffer(blobFile);

    return promise;
};

const ExportStudyDownloadCanceledError = (message) => {
  this.name = 'ExportStudyDownloadCanceledError';
  this.message = message || 'Download canceled';
  this.stack = (new Error()).stack;
}
ExportStudyDownloadCanceledError.prototype = Object.create(Error.prototype);
ExportStudyDownloadCanceledError.prototype.constructor = ExportStudyDownloadCanceledError;
<|endoftext|>


<commit_before>const assign = require('object-assign')
const validate = require('../validators')

function DatasetsClient(client) {
  this.request = client.request.bind(client)
}

assign(DatasetsClient.prototype, {
  create(name) {
    return this._modify('PUT', name)
  },

  delete(name) {
    return this._modify('DELETE', name)
  },

  list() {
    return this.request({uri: '/datasets'})
  },

  _modify(method, name) {
    validate.dataset(name)
    return this.request({method, uri: `/datasets/${name}`})
  }
})

module.exports = DatasetsClient
<commit_msg>[client] Allow specifying a request body for dataset creation requests<commit_after>const assign = require('object-assign')
const validate = require('../validators')

function DatasetsClient(client) {
  this.request = client.request.bind(client)
}

assign(DatasetsClient.prototype, {
  create(name, options) {
    return this._modify('PUT', name, options)
  },

  delete(name) {
    return this._modify('DELETE', name)
  },

  list() {
    return this.request({uri: '/datasets'})
  },

  _modify(method, name, body) {
    validate.dataset(name)
    return this.request({method, uri: `/datasets/${name}`, body})
  }
})

module.exports = DatasetsClient
<|endoftext|>


<commit_before>var getContext = function(display, infos, curLevel) {

    var language_strings = {
        en: {
            categories: {
                barcode: 'Barcode'
            },
            label: {
                getPixelLuminosity: 'getPixelLuminosity(%1, %2)',
                setPixelLuminosity: 'setPixelLuminosity(%1, %2, %3)',
                width: 'width()',
                height: 'height()',
                printResult: 'printResult(%1)'
            },
            code: {
                getPixelLuminosity: 'getPixelLuminosity',
                setPixelLuminosity: 'setPixelLuminosity',
                width: 'width',
                height: 'height',
                printResult: 'printResult'
            },
            description: {
                getPixelLuminosity: 'getPixelLuminosity',
                setPixelLuminosity: 'setPixelLuminosity',
                width: 'width',
                height: 'height',
                printResult: 'printResult description'
            },
            startingBlockName: "Program",
            messages: {
                success: 'Success',
                mistake: 'Mistake',
                result: 'Result:'
            },
            ui: {
            }
        }
    }

    var context = quickAlgoContext(display, infos)
    context.inlinePopupMessage = true;
    var strings = context.setLocalLanguageStrings(language_strings)

    /*
    var conceptBaseUrl = window.location.protocol + '//static4.castor-informatique.fr/help/index.html';
    context.conceptList = [
        {id: 'test', name: 'test', url: conceptBaseUrl+'#test'},
    ];
    */

    var ready = false;

    context.reset = function(taskInfos) {
        //console.log('context.reset', context.display, taskInfos)

        if(!ready) {
            $('#grid').empty();

            context.barcodeDisplay = BarcodeDisplay({
                parent: $('#grid'),
                image: taskInfos.image
            });

            context.userDisplay = UserDisplay({
                parent: $('#grid'),
                size: context.barcodeDisplay.getSize()
            });

            $(window).resize(function() {
                context.barcodeDisplay && context.barcodeDisplay.resize();
                context.userDisplay && context.userDisplay.resize();
            });
        }
        ready = true;

        if(taskInfos) {
            context.valid_result = taskInfos.valid_result || {};
            context.barcodeDisplay.loadImage(taskInfos.image, function() {
                context.userDisplay.setSize(context.barcodeDisplay.getSize());
            });
        }

        context.barcodeDisplay && context.barcodeDisplay.resetCursor();
    }

    context.setScale = function(scale) {}
    context.updateScale = function() {}
    context.resetDisplay = function() {}
    context.unload = function() {}

    var result = {

        data: '',
        element: null,

        init: function() {
            var el = $('#barcode-result');
            if(el.length == 0) {
                this.element = $('')
                var wrapper = $('
' + strings.messages.result + '
');
                wrapper.append(this.element)
                $('#grid').append(wrapper);
            }
        },

        set: function(str) {
            this.init();
            this.data = str;
            this.element && this.element.html(str);
        }
    }

    context.gradeResult = function() {
        if(result.data == context.valid_result.data) {
            context.success = true;
            throw(strings.messages.success);
            return;
        }
        context.success = false;
        throw new Error(strings.messages.mistake);
    }

    context.barcode = {

        getPixelLuminosity: function(x, y, callback) {
            context.waitDelay(callback, context.barcodeDisplay.getPixelLuminosity(x, y));
        },

        setPixelLuminosity: function(x, y, v, callback) {
            context.waitDelay(callback, context.userDisplay.setPixelLuminosity(x, y, v));
        },

        width: function(callback) {
            context.waitDelay(callback, function() {
                return context.barcodeDisplay.size().width
            });
        },

        height: function(callback) {
            context.waitDelay(callback, function() {
                return context.barcodeDisplay.getSize().height
            });
        },

        printResult: function(v, callback) {
            context.waitDelay(callback, result.set(v));
        },
    }

    context.customBlocks = {
        barcode: {
            barcode: [
                { name: 'getPixelLuminosity',
                    params: ['Number', 'Number'],
                    params_names: ['x', 'y'],
                    yieldsValue: true
                },
                { name: 'setPixelLuminosity',
                    params: ['Number', 'Number', 'String'],
                    params_names: ['x', 'y', 'value'],
                    yieldsValue: true
                },
                { name: 'width',
                    params: [],
                    params_names: [],
                    yieldsValue: true
                },
                { name: 'height',
                    params: [],
                    params_names: [],
                    yieldsValue: true
                },
                { name: 'printResult',
                    params: ['String'],
                    params_names: ['v']
                },
            ]
        }
    }

    var typeData = {
        'Number': { bType: 'input_value', vType: 'math_number', fName: 'NUM', defVal: 0 },
        'String': { bType: 'input_value', vType: 'text', fName: 'TEXT', defVal: '' },
        'Block': { bType: 'input_value', fName: 'BLOCK', defVal: '' },
    }
    BlocksHelper.convertBlocks(context, 'barcode', typeData);
    return context;
}

if(window.quickAlgoLibraries) {
    quickAlgoLibraries.register('barcode', getContext);
} else {
    if(!window.quickAlgoLibrariesList) { window.quickAlgoLibrariesList = []; }
    window.quickAlgoLibrariesList.push(['barcode', getContext]);
}<commit_msg>barcode<commit_after>var getContext = function(display, infos, curLevel) {

    var language_strings = {
        en: {
            categories: {
                barcode: 'Barcode'
            },
            label: {
                getPixelLuminosity: 'getPixelLuminosity(%1, %2)',
                setPixelLuminosity: 'setPixelLuminosity(%1, %2, %3)',
                width: 'width()',
                height: 'height()',
                printResult: 'printResult(%1)'
            },
            code: {
                getPixelLuminosity: 'getPixelLuminosity',
                setPixelLuminosity: 'setPixelLuminosity',
                width: 'width',
                height: 'height',
                printResult: 'printResult'
            },
            description: {
                getPixelLuminosity: '',
                setPixelLuminosity: '',
                width: '',
                height: '',
                printResult: ''
            },
            startingBlockName: "Program",
            messages: {
                success: 'Success',
                mistake: 'Mistake',
                result: 'Result:'
            },
            ui: {
            }
        }
    }

    var context = quickAlgoContext(display, infos)
    context.inlinePopupMessage = true;
    var strings = context.setLocalLanguageStrings(language_strings)

    /*
    var conceptBaseUrl = window.location.protocol + '//static4.castor-informatique.fr/help/index.html';
    context.conceptList = [
        {id: 'test', name: 'test', url: conceptBaseUrl+'#test'},
    ];
    */

    var ready = false;

    context.reset = function(taskInfos) {
        //console.log('context.reset', context.display, taskInfos)

        if(!ready) {
            $('#grid').empty();

            context.barcodeDisplay = BarcodeDisplay({
                parent: $('#grid'),
                image: taskInfos.image
            });

            context.userDisplay = UserDisplay({
                parent: $('#grid'),
                size: context.barcodeDisplay.getSize()
            });

            $(window).resize(function() {
                context.barcodeDisplay && context.barcodeDisplay.resize();
                context.userDisplay && context.userDisplay.resize();
            });
        }
        ready = true;

        if(taskInfos) {
            context.valid_result = taskInfos.valid_result || {};
            context.barcodeDisplay.loadImage(taskInfos.image, function() {
                context.userDisplay.setSize(context.barcodeDisplay.getSize());
            });
        }

        context.barcodeDisplay && context.barcodeDisplay.resetCursor();
    }

    context.setScale = function(scale) {}
    context.updateScale = function() {}
    context.resetDisplay = function() {}
    context.unload = function() {}

    var result = {

        data: '',
        element: null,

        init: function() {
            var el = $('#barcode-result');
            if(el.length == 0) {
                this.element = $('')
                var wrapper = $('
' + strings.messages.result + '
');
                wrapper.append(this.element)
                $('#grid').append(wrapper);
            }
        },

        set: function(str) {
            this.init();
            this.data = str;
            this.element && this.element.html(str);
        }
    }

    context.gradeResult = function() {
        if(result.data == context.valid_result.data) {
            context.success = true;
            throw(strings.messages.success);
            return;
        }
        context.success = false;
        throw new Error(strings.messages.mistake);
    }

    context.barcode = {

        getPixelLuminosity: function(x, y, callback) {
            context.waitDelay(callback, context.barcodeDisplay.getPixelLuminosity(x, y));
        },

        setPixelLuminosity: function(x, y, v, callback) {
            context.waitDelay(callback, context.userDisplay.setPixelLuminosity(x, y, v));
        },

        width: function(callback) {
            context.waitDelay(callback, function() {
                return context.barcodeDisplay.size().width
            });
        },

        height: function(callback) {
            context.waitDelay(callback, function() {
                return context.barcodeDisplay.getSize().height
            });
        },

        printResult: function(v, callback) {
            context.waitDelay(callback, result.set(v));
        },
    }

    context.customBlocks = {
        barcode: {
            barcode: [
                { name: 'getPixelLuminosity',
                    params: ['Number', 'Number'],
                    params_names: ['x', 'y'],
                    yieldsValue: true
                },
                { name: 'setPixelLuminosity',
                    params: ['Number', 'Number', 'String'],
                    params_names: ['x', 'y', 'value'],
                    yieldsValue: true
                },
                { name: 'width',
                    params: [],
                    params_names: [],
                    yieldsValue: true
                },
                { name: 'height',
                    params: [],
                    params_names: [],
                    yieldsValue: true
                },
                { name: 'printResult',
                    params: ['String'],
                    params_names: ['v']
                },
            ]
        }
    }

    var typeData = {
        'Number': { bType: 'input_value', vType: 'math_number', fName: 'NUM', defVal: 0 },
        'String': { bType: 'input_value', vType: 'text', fName: 'TEXT', defVal: '' },
        'Block': { bType: 'input_value', fName: 'BLOCK', defVal: '' },
    }
    BlocksHelper.convertBlocks(context, 'barcode', typeData);
    return context;
}

if(window.quickAlgoLibraries) {
    quickAlgoLibraries.register('barcode', getContext);
} else {
    if(!window.quickAlgoLibrariesList) { window.quickAlgoLibrariesList = []; }
    window.quickAlgoLibrariesList.push(['barcode', getContext]);
}<|endoftext|>


<commit_before>import Ember from 'ember';
import config from 'ember-get-config';
import Analytics from '../mixins/analytics';

import { elasticEscape } from '../utils/elastic-query';

var getProvidersPayload = '{"from": 0,"query": {"bool": {"must": {"query_string": {"query": "*"}}, "filter": [{"term": {"types": "preprint"}}]}},"aggregations": {"sources": {"terms": {"field": "sources","size": 200}}}}';

const filterMap = {
    providers: 'sources',
    subjects: 'subjects'
};

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

/**
 * @class Discover Controller
 */
export default Ember.Controller.extend(Analytics, {
    theme: Ember.inject.service(), // jshint ignore:line
    // TODO: either remove or add functionality to info icon on "Refine your search panel"

    // Many pieces taken from: https://github.com/CenterForOpenScience/ember-share/blob/develop/app/controllers/discover.js
    queryParams: {
        page: 'page',
        queryString: 'q',
        subjectFilter: 'subject',
        providerFilter: 'provider',
    },

    activeFilters: { providers: [], subjects: [] },

    osfProviders: [
        'OSF',
        'PsyArXiv',
        'SocArXiv',
        'engrXiv'
    ],

    whiteListedProviders: [
        'OSF',
        'arXiv',
        'bioRxiv',
        'Cogprints',
        'engrXiv',
        'PeerJ',
        'PsyArXiv',
        'Research Papers in Economics',
        'SocArXiv'
    ].map(item => item.toLowerCase()),

    page: 1,
    size: 10,
    numberOfResults: 0,
    queryString: '',
    subjectFilter: null,
    queryBody: {},
    providersPassed: false,

    sortByOptions: ['Relevance', 'Upload date (oldest to newest)', 'Upload date (newest to oldest)'],

    treeSubjects: Ember.computed('activeFilters', function() {
        return this.get('activeFilters.subjects').slice();
    }),
    // chosenOption is always the first element in the list
    chosenSortByOption: Ember.computed('sortByOptions', function() {
        return this.get('sortByOptions')[0];
    }),

    showActiveFilters: true, //should always have a provider, don't want to mix osfProviders and non-osf
    showPrev: Ember.computed.gt('page', 1),
    showNext: Ember.computed('page', 'size', 'numberOfResults', function() {
        return this.get('page') * this.get('size') < this.get('numberOfResults');
    }),

    results: Ember.ArrayProxy.create({ content: [] }),

    searchUrl: config.SHARE.searchUrl,

    init() {
        this._super(...arguments);
        this.set('facetFilters', Ember.Object.create());

        Ember.$.ajax({
            type: 'POST',
            url: this.get('searchUrl'),
            data: getProvidersPayload,
            contentType: 'application/json',
            crossDomain: true,
        }).then(results => {
            const hits = results.aggregations.sources.buckets;
            const whiteList = this.get('whiteListedProviders');
            const providers = hits
                .filter(hit => whiteList.includes(hit.key.toLowerCase()));

            providers.push(
                ...this.get('osfProviders')
                .filter(key => !providers
                    .find(hit => hit.key.toLowerCase() === key.toLowerCase())
                )
                .map(key => ({
                    key,
                    doc_count: 0
                }))
            );

            providers
                .sort((a, b) => a.key.toLowerCase() < b.key.toLowerCase() ? -1 : 1)
                .unshift(
                    ...providers.splice(
                        providers.findIndex(item => item.key === 'OSF'),
                        1
                    )
                );

            if (!this.get('theme.isProvider')) {
                this.set('otherProviders', providers);
            } else {
                const filtered = providers.filter(
                    item => item.key.toLowerCase() === this.get('theme.id').toLowerCase()
                );

                this.set('otherProviders', filtered);
                this.get('activeFilters.providers').pushObject(filtered[0].key);
            }

            this.notifyPropertyChange('otherProviders');
        });

        this.loadPage();
    },
    subjectChanged: Ember.observer('subjectFilter', function() {
        Ember.run.once(() => {
            let filter = this.get('subjectFilter');
            if (!filter || filter === 'true') return;
            this.set('activeFilters.subjects', filter.split('AND'));
            this.notifyPropertyChange('activeFilters');
            this.loadPage();
        });
    }),
    providerChanged: Ember.observer('providerFilter', function() {
        if (!this.get('theme.isProvider')) {
            Ember.run.once(() => {
                let filter = this.get('providerFilter');
                if (!filter || filter === 'true') return;
                this.set('activeFilters.providers', filter.split('AND'));
                this.notifyPropertyChange('activeFilters');
                this.set('providersPassed', true);
                this.loadPage();
            });
        }
    }),
    loadPage() {
        this.set('loading', true);
        Ember.run.debounce(this, this._loadPage, 500);
    },
    _loadPage() {
        let queryBody = JSON.stringify(this.getQueryBody());

        return Ember.$.ajax({
            url: this.get('searchUrl'),
            crossDomain: true,
            type: 'POST',
            contentType: 'application/json',
            data: queryBody
        }).then(json => {
            if (this.isDestroyed || this.isDestroying) return;

            this.set('numberOfResults', json.hits.total);

            let results = json.hits.hits.map(hit => {
                // HACK: Make share data look like apiv2 preprints data
                let result = Ember.merge(hit._source, {
                    id: hit._id,
                    type: 'elastic-search-result',
                    workType: hit._source['@type'],
                    abstract: hit._source.description,
                    subjects: hit._source.subjects.map(each => ({text: each})),
                    providers: hit._source.sources.map(item => ({name: item})),
                    osfProvider: hit._source.sources.reduce((acc, source) => (acc || this.get('osfProviders').includes(source)), false),
                    hyperLinks: [// Links that are hyperlinks from hit._source.lists.links
                        {
                            type: 'share',
                            url: config.SHARE.baseUrl + 'preprint/' + hit._id
                        }
                    ],
                    infoLinks: [] // Links that are not hyperlinks  hit._source.lists.links
                });

                hit._source.identifiers.forEach(function(identifier) {
                    if (identifier.startsWith('http://')) {
                        result.hyperLinks.push({url: identifier});
                    } else {
                        const spl = identifier.split('://');
                        const [type, uri, ..._] = spl; // jshint ignore:line
                        result.infoLinks.push({type, uri});
                    }
                });

                result.contributors = result.lists.contributors
                  .sort((b, a) => (b.order_cited || -1) - (a.order_cited || -1))
                  .map(contributor => ({
                        users: Object.keys(contributor)
                          .reduce(
                              (acc, key) => Ember.merge(acc, {[key.camelize()]: contributor[key]}),
                              {bibliographic: contributor.relation !== 'contributor'}
                          )
                    }));

                // Temporary fix to handle half way migrated SHARE ES
                // Only false will result in a false here.
                result.contributors.map(contributor => contributor.users.bibliographic = !(contributor.users.bibliographic === false));  // jshint ignore:line

                return result;
            });

            this.set('loading', false);
            return this.set('results', results);
        });
    },
    maxPages: Ember.computed('numberOfResults', function() {
        return ((this.get('numberOfResults') / this.get('size')) | 0) + (this.get('numberOfResults') % 10 === 0 ? 0 : 1);
    }),
    getQueryBody() {
        const facetFilters = this.get('activeFilters');

        this.set('subjectFilter', facetFilters.subjects.join('AND'));

        if (!this.get('theme.isProvider'))
            this.set('providerFilter', facetFilters.providers.join('AND'));

        const filter = [
            {
                terms: {
                    type: [
                        'preprint'
                    ]
                }
            }
        ];

        // TODO set up ember to transpile Object.entries
        for (const key in filterMap) {
            const val = filterMap[key];
            const filterList = facetFilters[key];

            if (!filterList.length || (key === 'providers' && this.get('theme.isProvider')))
                continue;

            filter.push({
                terms: {
                    [val]: filterList
                }
            });
        }

        if (this.get('theme.isProvider')) {
            filter.push({
                terms: {
                    sources: [this.get('theme.provider.name')]
                }
            });
        }

        const sortByOption = this.get('chosenSortByOption');
        const sort = {};

        if (sortByOption === 'Upload date (oldest to newest)') {
            sort.date_updated = 'asc';
        } else if (sortByOption === 'Upload date (newest to oldest)') {
            sort.date_updated = 'desc';
        }

        return this.set('queryBody', {
            query: {
                bool: {
                    must: {
                        query_string: {
                            query: elasticEscape(this.get('queryString')) || '*'
                        }
                    },
                    filter
                }
            },
            sort,
            from: (this.get('page') - 1) * this.get('size'),
        });
    },

    reloadSearch: Ember.observer('activeFilters', function() {
        this.set('page', 1);
        this.loadPage();
    }),

    _clearFilters() {
        this.set('activeFilters', {
            providers: this.get('theme.isProvider') ? this.get('activeFilters.providers') : [],
            subjects: []
        });
    },

    otherProviders: [],
    actions: {
        search(val, event) {
            if (event &&
                (
                    event.keyCode < 49 ||
                    [91, 92, 93].includes(event.keyCode)
                ) &&
                ![8, 32, 48].includes(event.keyCode)
            )
                return;

            this.set('page', 1);
            this.loadPage();

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: `${event && event.type === 'keyup' ? 'input' : 'button'}`,
                    action: `${event && event.type === 'keyup' ? 'onkeyup' : 'click'}`,
                    label: 'Preprints - Discover - Search'
                });
        },

        previous() {
            if (this.get('page') > 1) {
                this.decrementProperty('page');
                this.loadPage();
            }
        },

        next() {
            if (this.get('page') * this.get('size') <= this.get('numberOfResults')) {
                this.incrementProperty('page');
                this.loadPage();
            }
        },

        clearFilters() {
            this._clearFilters();

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: 'button',
                    action: 'click',
                    label: 'Preprints - Discover - Clear Filters'
                });
        },

        sortBySelect(index) {
            // Selecting an option just swaps it with whichever option is first
            let copy = this.get('sortByOptions').slice(0);
            let temp = copy[0];
            copy[0] = copy[index];
            copy[index] = temp;
            this.set('sortByOptions', copy);
            this.set('page', 1);
            this.loadPage();

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: 'dropdown',
                    action: 'select',
                    label: `Preprints - Discover - Sort by: ${copy[index]}`
                });
        },

        updateFilters(filterType, item) {
            item = typeof item === 'object' ? item.text : item;
            const filters = this.get(`activeFilters.${filterType}`);
            const hasItem = filters.includes(item);
            const action = hasItem ? 'remove' : 'push';
            filters[`${action}Object`](item);
            this.notifyPropertyChange('activeFilters');

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: 'filter',
                    action: hasItem ? 'remove' : 'add',
                    label: `Preprints - Discover - ${filterType} ${item}`
                });
        },
    },
});
<commit_msg>Use internationalization with sorting options<commit_after>import Ember from 'ember';
import config from 'ember-get-config';
import Analytics from '../mixins/analytics';

import { elasticEscape } from '../utils/elastic-query';

var getProvidersPayload = '{"from": 0,"query": {"bool": {"must": {"query_string": {"query": "*"}}, "filter": [{"term": {"types": "preprint"}}]}},"aggregations": {"sources": {"terms": {"field": "sources","size": 200}}}}';

const filterMap = {
    providers: 'sources',
    subjects: 'subjects'
};

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

/**
 * @class Discover Controller
 */
export default Ember.Controller.extend(Analytics, {
    theme: Ember.inject.service(), // jshint ignore:line
    // TODO: either remove or add functionality to info icon on "Refine your search panel"

    // Many pieces taken from: https://github.com/CenterForOpenScience/ember-share/blob/develop/app/controllers/discover.js
    queryParams: {
        page: 'page',
        queryString: 'q',
        subjectFilter: 'subject',
        providerFilter: 'provider',
    },

    activeFilters: { providers: [], subjects: [] },

    osfProviders: [
        'OSF',
        'PsyArXiv',
        'SocArXiv',
        'engrXiv'
    ],

    whiteListedProviders: [
        'OSF',
        'arXiv',
        'bioRxiv',
        'Cogprints',
        'engrXiv',
        'PeerJ',
        'PsyArXiv',
        'Research Papers in Economics',
        'SocArXiv'
    ].map(item => item.toLowerCase()),

    page: 1,
    size: 10,
    numberOfResults: 0,
    queryString: '',
    subjectFilter: null,
    queryBody: {},
    providersPassed: false,

    i18n: Ember.inject.service(),

    sortByOptions: Ember.computed('i18n.locale', 'i18n.locales', function() {
        const i18n = this.get('i18n');
        return [i18n.t('discover.relevance'), i18n.t('discover.sort_oldest_newest'), i18n.t('discover.sort_newest_oldest')];
    }),

    treeSubjects: Ember.computed('activeFilters', function() {
        return this.get('activeFilters.subjects').slice();
    }),
    // chosenOption is always the first element in the list
    chosenSortByOption: Ember.computed('sortByOptions', function() {
        return this.get('sortByOptions')[0];
    }),

    showActiveFilters: true, //should always have a provider, don't want to mix osfProviders and non-osf
    showPrev: Ember.computed.gt('page', 1),
    showNext: Ember.computed('page', 'size', 'numberOfResults', function() {
        return this.get('page') * this.get('size') < this.get('numberOfResults');
    }),

    results: Ember.ArrayProxy.create({ content: [] }),

    searchUrl: config.SHARE.searchUrl,

    init() {
        this._super(...arguments);
        this.set('facetFilters', Ember.Object.create());

        Ember.$.ajax({
            type: 'POST',
            url: this.get('searchUrl'),
            data: getProvidersPayload,
            contentType: 'application/json',
            crossDomain: true,
        }).then(results => {
            const hits = results.aggregations.sources.buckets;
            const whiteList = this.get('whiteListedProviders');
            const providers = hits
                .filter(hit => whiteList.includes(hit.key.toLowerCase()));

            providers.push(
                ...this.get('osfProviders')
                .filter(key => !providers
                    .find(hit => hit.key.toLowerCase() === key.toLowerCase())
                )
                .map(key => ({
                    key,
                    doc_count: 0
                }))
            );

            providers
                .sort((a, b) => a.key.toLowerCase() < b.key.toLowerCase() ? -1 : 1)
                .unshift(
                    ...providers.splice(
                        providers.findIndex(item => item.key === 'OSF'),
                        1
                    )
                );

            if (!this.get('theme.isProvider')) {
                this.set('otherProviders', providers);
            } else {
                const filtered = providers.filter(
                    item => item.key.toLowerCase() === this.get('theme.id').toLowerCase()
                );

                this.set('otherProviders', filtered);
                this.get('activeFilters.providers').pushObject(filtered[0].key);
            }

            this.notifyPropertyChange('otherProviders');
        });

        this.loadPage();
    },
    subjectChanged: Ember.observer('subjectFilter', function() {
        Ember.run.once(() => {
            let filter = this.get('subjectFilter');
            if (!filter || filter === 'true') return;
            this.set('activeFilters.subjects', filter.split('AND'));
            this.notifyPropertyChange('activeFilters');
            this.loadPage();
        });
    }),
    providerChanged: Ember.observer('providerFilter', function() {
        if (!this.get('theme.isProvider')) {
            Ember.run.once(() => {
                let filter = this.get('providerFilter');
                if (!filter || filter === 'true') return;
                this.set('activeFilters.providers', filter.split('AND'));
                this.notifyPropertyChange('activeFilters');
                this.set('providersPassed', true);
                this.loadPage();
            });
        }
    }),
    loadPage() {
        this.set('loading', true);
        Ember.run.debounce(this, this._loadPage, 500);
    },
    _loadPage() {
        let queryBody = JSON.stringify(this.getQueryBody());

        return Ember.$.ajax({
            url: this.get('searchUrl'),
            crossDomain: true,
            type: 'POST',
            contentType: 'application/json',
            data: queryBody
        }).then(json => {
            if (this.isDestroyed || this.isDestroying) return;

            this.set('numberOfResults', json.hits.total);

            let results = json.hits.hits.map(hit => {
                // HACK: Make share data look like apiv2 preprints data
                let result = Ember.merge(hit._source, {
                    id: hit._id,
                    type: 'elastic-search-result',
                    workType: hit._source['@type'],
                    abstract: hit._source.description,
                    subjects: hit._source.subjects.map(each => ({text: each})),
                    providers: hit._source.sources.map(item => ({name: item})),
                    osfProvider: hit._source.sources.reduce((acc, source) => (acc || this.get('osfProviders').includes(source)), false),
                    hyperLinks: [// Links that are hyperlinks from hit._source.lists.links
                        {
                            type: 'share',
                            url: config.SHARE.baseUrl + 'preprint/' + hit._id
                        }
                    ],
                    infoLinks: [] // Links that are not hyperlinks  hit._source.lists.links
                });

                hit._source.identifiers.forEach(function(identifier) {
                    if (identifier.startsWith('http://')) {
                        result.hyperLinks.push({url: identifier});
                    } else {
                        const spl = identifier.split('://');
                        const [type, uri, ..._] = spl; // jshint ignore:line
                        result.infoLinks.push({type, uri});
                    }
                });

                result.contributors = result.lists.contributors
                  .sort((b, a) => (b.order_cited || -1) - (a.order_cited || -1))
                  .map(contributor => ({
                        users: Object.keys(contributor)
                          .reduce(
                              (acc, key) => Ember.merge(acc, {[key.camelize()]: contributor[key]}),
                              {bibliographic: contributor.relation !== 'contributor'}
                          )
                    }));

                // Temporary fix to handle half way migrated SHARE ES
                // Only false will result in a false here.
                result.contributors.map(contributor => contributor.users.bibliographic = !(contributor.users.bibliographic === false));  // jshint ignore:line

                return result;
            });

            this.set('loading', false);
            return this.set('results', results);
        });
    },
    maxPages: Ember.computed('numberOfResults', function() {
        return ((this.get('numberOfResults') / this.get('size')) | 0) + (this.get('numberOfResults') % 10 === 0 ? 0 : 1);
    }),
    getQueryBody() {
        const facetFilters = this.get('activeFilters');

        this.set('subjectFilter', facetFilters.subjects.join('AND'));

        if (!this.get('theme.isProvider'))
            this.set('providerFilter', facetFilters.providers.join('AND'));

        const filter = [
            {
                terms: {
                    type: [
                        'preprint'
                    ]
                }
            }
        ];

        // TODO set up ember to transpile Object.entries
        for (const key in filterMap) {
            const val = filterMap[key];
            const filterList = facetFilters[key];

            if (!filterList.length || (key === 'providers' && this.get('theme.isProvider')))
                continue;

            filter.push({
                terms: {
                    [val]: filterList
                }
            });
        }

        if (this.get('theme.isProvider')) {
            filter.push({
                terms: {
                    sources: [this.get('theme.provider.name')]
                }
            });
        }

        const sortByOption = this.get('chosenSortByOption');
        const sort = {};
        const i18n = this.get('i18n');
        if (sortByOption.toString() === i18n.t('discover.sort_oldest_newest').toString()) {
            sort.date_updated = 'asc';
        } else if (sortByOption.toString() === i18n.t('discover.sort_newest_oldest').toString()) {
            sort.date_updated = 'desc';
        }

        return this.set('queryBody', {
            query: {
                bool: {
                    must: {
                        query_string: {
                            query: elasticEscape(this.get('queryString')) || '*'
                        }
                    },
                    filter
                }
            },
            sort,
            from: (this.get('page') - 1) * this.get('size'),
        });
    },

    reloadSearch: Ember.observer('activeFilters', function() {
        this.set('page', 1);
        this.loadPage();
    }),

    _clearFilters() {
        this.set('activeFilters', {
            providers: this.get('theme.isProvider') ? this.get('activeFilters.providers') : [],
            subjects: []
        });
    },

    otherProviders: [],
    actions: {
        search(val, event) {
            if (event &&
                (
                    event.keyCode < 49 ||
                    [91, 92, 93].includes(event.keyCode)
                ) &&
                ![8, 32, 48].includes(event.keyCode)
            )
                return;

            this.set('page', 1);
            this.loadPage();

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: `${event && event.type === 'keyup' ? 'input' : 'button'}`,
                    action: `${event && event.type === 'keyup' ? 'onkeyup' : 'click'}`,
                    label: 'Preprints - Discover - Search'
                });
        },

        previous() {
            if (this.get('page') > 1) {
                this.decrementProperty('page');
                this.loadPage();
            }
        },

        next() {
            if (this.get('page') * this.get('size') <= this.get('numberOfResults')) {
                this.incrementProperty('page');
                this.loadPage();
            }
        },

        clearFilters() {
            this._clearFilters();

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: 'button',
                    action: 'click',
                    label: 'Preprints - Discover - Clear Filters'
                });
        },

        sortBySelect(index) {
            // Selecting an option just swaps it with whichever option is first
            let copy = this.get('sortByOptions').slice(0);
            let temp = copy[0];
            copy[0] = copy[index];
            copy[index] = temp;
            this.set('sortByOptions', copy);
            this.set('page', 1);
            this.loadPage();

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: 'dropdown',
                    action: 'select',
                    label: `Preprints - Discover - Sort by: ${copy[index]}`
                });
        },

        updateFilters(filterType, item) {
            item = typeof item === 'object' ? item.text : item;
            const filters = this.get(`activeFilters.${filterType}`);
            const hasItem = filters.includes(item);
            const action = hasItem ? 'remove' : 'push';
            filters[`${action}Object`](item);
            this.notifyPropertyChange('activeFilters');

            Ember.get(this, 'metrics')
                .trackEvent({
                    category: 'filter',
                    action: hasItem ? 'remove' : 'add',
                    label: `Preprints - Discover - ${filterType} ${item}`
                });
        },
    },
});
<|endoftext|>


<commit_before>
var express    = require('express');
var bodyParser = require('body-parser');
//Node module to specify server info
//var http = require('http');
//Node file system module
//var fs = require('fs');
//var stream = require('stream');
//var request = require('request');
//var es = require('event-stream');
//var JSONstream = require('JSONstream');

//Package to parse multibody forms
//var formidable = require("formidable");

/*
// create server called "server"
var server = http.createServer(function (req, res) {

    //GET requests are processed by "displayForm" code
    if (req.method.toLowerCase() == 'get') {
        displayForm(res);
    }

    //POST requests are processed by "submittingForm" code
    else if (req.method.toLowerCase() == 'post') {
        submittingForm(req, res);
    }

});

var jsonfile = require('jsonfile')

var file = 'package.json'
jsonfile.readFile(file, function(err, obj) {
  console.dir(obj)
})

var file = 'data.json'
var obj = {name: 'JP'}

jsonfile.writeFile(file, obj, function (err) {
  console.error(err)
})

function displayForm(res) {

    fs.readFile('form.html', function (err, data) {
        res.writeHead(200, {
            'Content-Type': 'text/html',
                'Content-Length': data.length
        });
        res.write(data);
        res.end();
    });

};

function submittingForm(req, res) {

	// creates new incoming form just called 'form'
    var form = new formidable.IncomingForm();

    form.encoding = 'utf-8';

    // where uploaded files are stored
    form.uploadDir = "/examples";

    //parses incoming request called "req" and the callback on it is function "example"
    form.parse(req, exampleFunction);

};

function exampleFunction(err, fields, files) {

    // sends a header message back with (statusCode[, statusMessage][, headers])
        res.writeHead(200, {
            'content-type': 'text/plain'
        });

    //writeFile(filename, obj, [options], callback)
	    jsonfile.writeFile("image.json", obj, function (err) {
		  console.error(err)
		})

    // tells the server that this callback has ended
        res.end();
    };
*/

var Bear     = require('./examples/bear');

var app        = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var port = process.env.PORT || 8080;

var mongoose   = require('mongoose');
mongoose.connect('mongodb://localhost:27017/testMongoDB'); // connect to our database

//API ROUTES

var router = express.Router();

// middleware to use for all requests
router.use(function(req, res, next) {
    // do logging
    console.log('Something is happening.');
    next(); // make sure we go to the next routes and don't stop here
});

//testing testing
/*router.get('/', function(req, res) {

    res.json({ message: 'hooray! welcome to our api!' });

});*/

//route /api/vectors GET
//get all vector JSONs

//route /api/vectors POST
//add a single new vector JSON

//route /api/vectors/:vector_id GET
//get specific vector JSON

//route /api/vectors/:vector_id PUT
//update vector JSON with new info

//route /api/vectors/:vector_id DELETE
//delete vector JSON
/*
router.route('/bears')

    // create a bear (accessed at POST http://localhost:8080/api/bears)
    .post(function(req, res) {

        var bear = new Bear();      // create a new instance of the Bear model
        bear.name = req.body.name;  // set the bears name (comes from the request)

        // save the bear and check for errors
        bear.save(function(err) {
            if (err)
                res.send(err);

            res.json({ message: 'Bear created!' });
        });
    });
*/
/*
    .get(function(req, res) {
        Bear.find(function(err, bears) {
            if (err)
                res.send(err);

            res.json(bears);
        });

    });

    .get(function(req, res) {
        Bear.findById(req.params.bear_id, function(err, bear) {
            if (err)
                res.send(err);
            res.json(bear);
        });
    });

    .put(function(req, res) {

        // use our bear model to find the bear we want
        Bear.findById(req.params.bear_id, function(err, bear) {

            if (err)
                res.send(err);

            bear.name = req.body.name;  // update the bears info

            // save the bear
            bear.save(function(err) {
                if (err)
                    res.send(err);

                res.json({ message: 'Bear updated!' });
            });

        });
    });
*/
app.use('/api', router);

//tells the server where it should be listening for req and res
app.listen(8080);

<commit_msg>still playing around with testing basic RESTful api stuff and then hopefully will get posting jsons in response to the two main triggers in the main app<commit_after>
var express    = require('express');
var bodyParser = require('body-parser');
var mongoose   = require('mongoose');
var jsonfile = require('jsonfile')
//Node module to specify server info
//var http = require('http');
//Node file system module
//var fs = require('fs');
//var stream = require('stream');
//var request = require('request');
//var es = require('event-stream');
//var JSONstream = require('JSONstream');

//Package to parse multibody forms
//var formidable = require("formidable");

/*
// create server called "server"
var server = http.createServer(function (req, res) {

    //GET requests are processed by "displayForm" code
    if (req.method.toLowerCase() == 'get') {
        displayForm(res);
    }

    //POST requests are processed by "submittingForm" code
    else if (req.method.toLowerCase() == 'post') {
        submittingForm(req, res);
    }

});

var file = 'package.json'
jsonfile.readFile(file, function(err, obj) {
  console.dir(obj)
})

var file = 'data.json'
var obj = {name: 'JP'}

jsonfile.writeFile(file, obj, function (err) {
  console.error(err)
})

function displayForm(res) {

    fs.readFile('form.html', function (err, data) {
        res.writeHead(200, {
            'Content-Type': 'text/html',
                'Content-Length': data.length
        });
        res.write(data);
        res.end();
    });

};

function submittingForm(req, res) {

	// creates new incoming form just called 'form'
    var form = new formidable.IncomingForm();

    form.encoding = 'utf-8';

    // where uploaded files are stored
    form.uploadDir = "/examples";

    //parses incoming request called "req" and the callback on it is function "example"
    form.parse(req, exampleFunction);

};

function exampleFunction(err, fields, files) {

    // sends a header message back with (statusCode[, statusMessage][, headers])
        res.writeHead(200, {
            'content-type': 'text/plain'
        });

    //writeFile(filename, obj, [options], callback)
	    jsonfile.writeFile("image.json", obj, function (err) {
		  console.error(err)
		})

    // tells the server that this callback has ended
        res.end();
    };
*/

var Bear     = require('./examples/bear');

var app        = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var port = process.env.PORT || 8080;

mongoose.connect('mongodb://localhost:27017/testMongoDB'); // connect to our database

//API ROUTES

var router = express.Router();

// middleware to use for all requests
router.use(function(req, res, next) {
    // do logging
    console.log('Something is happening.');
    next(); // make sure we go to the next routes and don't stop here
});

//testing testing
router.get('/', function(req, res) {

    res.json({ message: 'hooray! welcome to our api!' });

});

//route /api/vectors GET
//get all vector JSONs

//route /api/vectors POST
//add a single new vector JSON

//route /api/vectors/:vector_id GET
//get specific vector JSON

//route /api/vectors/:vector_id PUT
//update vector JSON with new info

//route /api/vectors/:vector_id DELETE
//delete vector JSON

    // create a bear (accessed at POST http://localhost:8080/api/bears)
    router.post('/bears', function(req, res) {

        var bear = new Bear();      // create a new instance of the Bear model
        bear.name = req.body.name;  // set the bears name (comes from the request)

        // save the bear and check for errors
        bear.save(function(err) {
            if (err)
                res.send(err);

            res.json({ message: 'Bear created!' });
        });
    });

    router.get('/bears', function(req, res) {
        Bear.find(function(err, bears) {
            if (err)
                res.send(err);

            res.json(bears);
        });

    });

    router.get('/bears/:bear_id', function(req, res) {
        Bear.findById(req.params.bear_id, function(err, bear) {
            if (err)
                res.send(err);
            res.json(bear);

        });

    });
/*

jsonfile.readFile(file, function(err, obj) {
  console.dir(obj)
})

    router.put('/bears/:bear_id', function(req, res) {

        // use our bear model to find the bear we want
        Bear.findById(req.params.bear_id, function(err, bear) {

            if (err)
                res.send(err);

            bear.name = req.body.name;  // update the bears info

            // save the bear
            bear.save(function(err) {
                if (err)
                    res.send(err);

                res.json({ message: 'Bear updated!' });
            });

        });
    });
*/
app.use('/api', router);

//tells the server where it should be listening for req and res
app.listen(8080);

<|endoftext|>


<commit_before>/* eslint no-console:0 */
/* eslint space-before-function-paren:0 */
// Module imports
import '@babel/polyfill';
import keytar from 'keytar';
import SemVer from 'semver';
import https from 'https';
import { app, dialog, ipcMain, session, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import { Lbry } from 'lbry-redux';
import Daemon from './Daemon';
import isDev from 'electron-is-dev';
import createTray from './createTray';
import createWindow from './createWindow';
import pjson from '../package.json';
import startSandbox from './startSandbox';
import installDevtools from './installDevtools';
import fs from 'fs';
import path from 'path';
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
let upgradeDisabled;
try {
  fs.accessSync(filePath, fs.constants.R_OK);
  upgradeDisabled = true;
} catch (err) {
  upgradeDisabled = false;
}
autoUpdater.autoDownload = !upgradeDisabled;

// This is set to true if an auto update has been downloaded through the Electron
// auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown.
let autoUpdateDownloaded = false;

// This is used to keep track of whether we are showing the special dialog
// that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false;

// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected.
let rendererWindow;

let tray; // eslint-disable-line
let daemon;

const appState = {};

app.setAsDefaultProtocolClient('lbry');
app.setName('LBRY');
app.setAppUserModelId('io.lbry.LBRY');

if (isDev) {
  // Disable security warnings in dev mode:
  // https://github.com/electron/electron/blob/master/docs/tutorial/security.md#electron-security-warnings
  process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
}

const startDaemon = async () => {
  let isDaemonRunning = false;

  await Lbry.status()
    .then(() => {
      isDaemonRunning = true;
      console.log('SDK already running');
    })
    .catch(() => {
      console.log('Starting SDK');
    });

  if (!isDaemonRunning) {
    daemon = new Daemon();
    daemon.on('exit', () => {
      if (!isDev) {
        daemon = null;
        if (!appState.isQuitting) {
          dialog.showErrorBox(
            'Daemon has Exited',
            'The daemon may have encountered an unexpected error, or another daemon instance is already running. \n\n' +
              'For more information please visit: \n' +
              'https://lbry.com/faq/startup-troubleshooting'
          );
        }
        app.quit();
      }
    });
    await daemon.launch();
  }
};

// When we are starting the app, ensure there are no other apps already running
const gotSingleInstanceLock = app.requestSingleInstanceLock();

if (!gotSingleInstanceLock) {
  // Another instance already has a lock, abort
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Send the url to the app to navigate first, then focus
    if (rendererWindow) {
      if ((process.platform === 'win32' || process.platform === 'linux') && String(argv[1]).startsWith('lbry')) {
        let URI = argv[1];

        // Keep only command line / deep linked arguments
        // Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
        // restore the original URI that was typed.
        //   - If the URI has no path, Windows adds a trailing slash. LBRY URIs can't have a slash with no
        //     path, so we just strip it off.
        //   - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
        //     an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
        //   - ? also interpreted as an anchor, remove slash also.
        if (process.platform === 'win32') {
          URI = URI.replace(/\/$/, '')
            .replace('/#', '#')
            .replace('/?', '?');
        }

        rendererWindow.webContents.send('open-uri-requested', URI);
      }

      rendererWindow.show();
    }
  });

  app.on('ready', async () => {
    await startDaemon();
    startSandbox();

    if (isDev) {
      await installDevtools();
    }

    rendererWindow = createWindow(appState);
    tray = createTray(rendererWindow);

    if (!isDev) {
      rendererWindow.webContents.on('devtools-opened', () => {
        // Send a message to the renderer process so we can log a security warning
        rendererWindow.webContents.send('devtools-is-opened');
      });
    }

    // HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
    // See https://github.com/electron/electron/issues/13008#issuecomment-400261941
    session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
      if (details.url.includes('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33')) {
        callback({
          redirectURL: details.url.replace(
            '7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
            '57c9d07b416b5a2ea23d28247300e4af36329bdc'
          ),
        });
      } else {
        callback({ cancel: false });
      }
    });
  });
}

app.on('activate', () => {
  if (rendererWindow) {
    rendererWindow.show();
  }
});

app.on('will-quit', event => {
  if (
    process.platform === 'win32' &&
    autoUpdateDownloaded &&
    !appState.autoUpdateAccepted &&
    !showingAutoUpdateCloseAlert
  ) {
    // We're on Win and have an update downloaded, but the user declined it (or closed
    // the app without accepting it). Now the user is closing the app, so the new update
    // will install. On Mac this is silent, but on Windows they get a confusing permission
    // escalation dialog, so we show Windows users a warning dialog first.

    showingAutoUpdateCloseAlert = true;
    dialog.showMessageBox(
      {
        type: 'info',
        title: 'LBRY Will Upgrade',
        message: 'LBRY has a pending upgrade. Please select "Yes" to install it on the prompt shown after this one.',
      },
      () => {
        app.quit();
      }
    );

    event.preventDefault();
    return;
  }

  appState.isQuitting = true;
  if (daemon) {
    daemon.quit();
    event.preventDefault();
  }

  if (rendererWindow) {
    rendererWindow = null;
  }
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, URL) => {
    event.preventDefault();

    if (rendererWindow) {
      rendererWindow.webContents.send('open-uri-requested', URL);
      rendererWindow.show();
    } else {
      appState.macDeepLinkingURI = URL;
    }
  });
});

app.on('before-quit', () => {
  appState.isQuitting = true;
});

ipcMain.on('upgrade', (event, installerPath) => {
  app.on('quit', () => {
    console.log('Launching upgrade installer at', installerPath);
    // This gets triggered called after *all* other quit-related events, so
    // we'll only get here if we're fully prepared and quitting for real.
    shell.openItem(installerPath);
  });
  // what to do if no shutdown in a long time?
  console.log('Update downloaded to', installerPath);
  console.log('The app will close and you will be prompted to install the latest version of LBRY.');
  console.log('After the install is complete, please reopen the app.');
  app.quit();
});

autoUpdater.on('update-downloaded', () => {
  autoUpdateDownloaded = true;
});

ipcMain.on('autoUpdateAccepted', () => {
  appState.autoUpdateAccepted = true;
  autoUpdater.quitAndInstall();
});

ipcMain.on('version-info-requested', () => {
  function formatRc(ver) {
    // Adds dash if needed to make RC suffix SemVer friendly
    return ver.replace(/([^-])rc/, '$1-rc');
  }

  const localVersion = pjson.version;
  let result = '';
  const onSuccess = res => {
    res.on('data', data => {
      result += data;
    });

    res.on('end', () => {
      let json;
      try {
        json = JSON.parse(result);
      } catch (e) {
        return;
      }
      const tagName = json.tag_name;
      if (tagName) {
        const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
        if (!remoteVersion) {
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', localVersion);
          }
        } else {
          const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', {
              remoteVersion,
              localVersion,
              upgradeAvailable,
            });
          }
        }
      } else if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', { localVersion });
      }
    });
  };

  const requestLatestRelease = (alreadyRedirected = false) => {
    const req = https.get(
      {
        hostname: 'api.github.com',
        path: '/repos/lbryio/lbry-desktop/releases/latest',
        headers: { 'user-agent': `LBRY/${localVersion}` },
      },
      res => {
        if (res.statusCode === 301 || res.statusCode === 302) {
          requestLatestRelease(res.headers.location, true);
        } else {
          onSuccess(res);
        }
      }
    );

    if (alreadyRedirected) return;
    req.on('error', err => {
      console.log('Failed to get current version from GitHub. Error:', err);
      if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', null);
      }
    });
  };

  if (upgradeDisabled && rendererWindow) {
    rendererWindow.webContents.send('version-info-received', { localVersion });
    return;
  }

  requestLatestRelease();
});

ipcMain.on('get-auth-token', event => {
  keytar.getPassword('LBRY', 'auth_token').then(token => {
    event.sender.send('auth-token-response', token ? token.toString().trim() : null);
  });
});

ipcMain.on('set-auth-token', (event, token) => {
  keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
});

ipcMain.on('delete-auth-token', (event, password) => {
  keytar.deletePassword('LBRY', 'auth_token', password).then(res => {
    event.sender.send('delete-auth-token-response', res);
  });
});

ipcMain.on('get-password', event => {
  keytar.getPassword('LBRY', 'wallet_password').then(password => {
    event.sender.send('get-password-response', password ? password.toString() : null);
  });
});

ipcMain.on('set-password', (event, password) => {
  if (password || password === '') {
    keytar.setPassword('LBRY', 'wallet_password', password).then(res => {
      event.sender.send('set-password-response', res);
    });
  }
});

ipcMain.on('delete-password', event => {
  keytar.deletePassword('LBRY', 'wallet_password').then(res => {
    event.sender.send('delete-password-response', res);
  });
});

process.on('uncaughtException', error => {
  console.log(error);
  dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
  appState.isQuitting = true;
  if (daemon) daemon.quit();
  app.exit(1);
});
<commit_msg>Force color profile to render correct colors<commit_after>/* eslint no-console:0 */
/* eslint space-before-function-paren:0 */
// Module imports
import '@babel/polyfill';
import keytar from 'keytar';
import SemVer from 'semver';
import https from 'https';
import { app, dialog, ipcMain, session, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import { Lbry } from 'lbry-redux';
import Daemon from './Daemon';
import isDev from 'electron-is-dev';
import createTray from './createTray';
import createWindow from './createWindow';
import pjson from '../package.json';
import startSandbox from './startSandbox';
import installDevtools from './installDevtools';
import fs from 'fs';
import path from 'path';
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
let upgradeDisabled;
try {
  fs.accessSync(filePath, fs.constants.R_OK);
  upgradeDisabled = true;
} catch (err) {
  upgradeDisabled = false;
}
autoUpdater.autoDownload = !upgradeDisabled;

// This is set to true if an auto update has been downloaded through the Electron
// auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown.
let autoUpdateDownloaded = false;

// This is used to keep track of whether we are showing the special dialog
// that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false;

// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected.
let rendererWindow;

let tray; // eslint-disable-line
let daemon;

const appState = {};

app.setAsDefaultProtocolClient('lbry');
app.setName('LBRY');
app.setAppUserModelId('io.lbry.LBRY');
app.commandLine.appendSwitch('force-color-profile', 'srgb');

if (isDev) {
  // Disable security warnings in dev mode:
  // https://github.com/electron/electron/blob/master/docs/tutorial/security.md#electron-security-warnings
  process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
}

const startDaemon = async () => {
  let isDaemonRunning = false;

  await Lbry.status()
    .then(() => {
      isDaemonRunning = true;
      console.log('SDK already running');
    })
    .catch(() => {
      console.log('Starting SDK');
    });

  if (!isDaemonRunning) {
    daemon = new Daemon();
    daemon.on('exit', () => {
      if (!isDev) {
        daemon = null;
        if (!appState.isQuitting) {
          dialog.showErrorBox(
            'Daemon has Exited',
            'The daemon may have encountered an unexpected error, or another daemon instance is already running. \n\n' +
              'For more information please visit: \n' +
              'https://lbry.com/faq/startup-troubleshooting'
          );
        }
        app.quit();
      }
    });
    await daemon.launch();
  }
};

// When we are starting the app, ensure there are no other apps already running
const gotSingleInstanceLock = app.requestSingleInstanceLock();

if (!gotSingleInstanceLock) {
  // Another instance already has a lock, abort
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Send the url to the app to navigate first, then focus
    if (rendererWindow) {
      if ((process.platform === 'win32' || process.platform === 'linux') && String(argv[1]).startsWith('lbry')) {
        let URI = argv[1];

        // Keep only command line / deep linked arguments
        // Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
        // restore the original URI that was typed.
        //   - If the URI has no path, Windows adds a trailing slash. LBRY URIs can't have a slash with no
        //     path, so we just strip it off.
        //   - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
        //     an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
        //   - ? also interpreted as an anchor, remove slash also.
        if (process.platform === 'win32') {
          URI = URI.replace(/\/$/, '')
            .replace('/#', '#')
            .replace('/?', '?');
        }

        rendererWindow.webContents.send('open-uri-requested', URI);
      }

      rendererWindow.show();
    }
  });

  app.on('ready', async () => {
    await startDaemon();
    startSandbox();

    if (isDev) {
      await installDevtools();
    }

    rendererWindow = createWindow(appState);
    tray = createTray(rendererWindow);

    if (!isDev) {
      rendererWindow.webContents.on('devtools-opened', () => {
        // Send a message to the renderer process so we can log a security warning
        rendererWindow.webContents.send('devtools-is-opened');
      });
    }

    // HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
    // See https://github.com/electron/electron/issues/13008#issuecomment-400261941
    session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
      if (details.url.includes('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33')) {
        callback({
          redirectURL: details.url.replace(
            '7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
            '57c9d07b416b5a2ea23d28247300e4af36329bdc'
          ),
        });
      } else {
        callback({ cancel: false });
      }
    });
  });
}

app.on('activate', () => {
  if (rendererWindow) {
    rendererWindow.show();
  }
});

app.on('will-quit', event => {
  if (
    process.platform === 'win32' &&
    autoUpdateDownloaded &&
    !appState.autoUpdateAccepted &&
    !showingAutoUpdateCloseAlert
  ) {
    // We're on Win and have an update downloaded, but the user declined it (or closed
    // the app without accepting it). Now the user is closing the app, so the new update
    // will install. On Mac this is silent, but on Windows they get a confusing permission
    // escalation dialog, so we show Windows users a warning dialog first.

    showingAutoUpdateCloseAlert = true;
    dialog.showMessageBox(
      {
        type: 'info',
        title: 'LBRY Will Upgrade',
        message: 'LBRY has a pending upgrade. Please select "Yes" to install it on the prompt shown after this one.',
      },
      () => {
        app.quit();
      }
    );

    event.preventDefault();
    return;
  }

  appState.isQuitting = true;
  if (daemon) {
    daemon.quit();
    event.preventDefault();
  }

  if (rendererWindow) {
    rendererWindow = null;
  }
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, URL) => {
    event.preventDefault();

    if (rendererWindow) {
      rendererWindow.webContents.send('open-uri-requested', URL);
      rendererWindow.show();
    } else {
      appState.macDeepLinkingURI = URL;
    }
  });
});

app.on('before-quit', () => {
  appState.isQuitting = true;
});

ipcMain.on('upgrade', (event, installerPath) => {
  app.on('quit', () => {
    console.log('Launching upgrade installer at', installerPath);
    // This gets triggered called after *all* other quit-related events, so
    // we'll only get here if we're fully prepared and quitting for real.
    shell.openItem(installerPath);
  });
  // what to do if no shutdown in a long time?
  console.log('Update downloaded to', installerPath);
  console.log('The app will close and you will be prompted to install the latest version of LBRY.');
  console.log('After the install is complete, please reopen the app.');
  app.quit();
});

autoUpdater.on('update-downloaded', () => {
  autoUpdateDownloaded = true;
});

ipcMain.on('autoUpdateAccepted', () => {
  appState.autoUpdateAccepted = true;
  autoUpdater.quitAndInstall();
});

ipcMain.on('version-info-requested', () => {
  function formatRc(ver) {
    // Adds dash if needed to make RC suffix SemVer friendly
    return ver.replace(/([^-])rc/, '$1-rc');
  }

  const localVersion = pjson.version;
  let result = '';
  const onSuccess = res => {
    res.on('data', data => {
      result += data;
    });

    res.on('end', () => {
      let json;
      try {
        json = JSON.parse(result);
      } catch (e) {
        return;
      }
      const tagName = json.tag_name;
      if (tagName) {
        const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
        if (!remoteVersion) {
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', localVersion);
          }
        } else {
          const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', {
              remoteVersion,
              localVersion,
              upgradeAvailable,
            });
          }
        }
      } else if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', { localVersion });
      }
    });
  };

  const requestLatestRelease = (alreadyRedirected = false) => {
    const req = https.get(
      {
        hostname: 'api.github.com',
        path: '/repos/lbryio/lbry-desktop/releases/latest',
        headers: { 'user-agent': `LBRY/${localVersion}` },
      },
      res => {
        if (res.statusCode === 301 || res.statusCode === 302) {
          requestLatestRelease(res.headers.location, true);
        } else {
          onSuccess(res);
        }
      }
    );

    if (alreadyRedirected) return;
    req.on('error', err => {
      console.log('Failed to get current version from GitHub. Error:', err);
      if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', null);
      }
    });
  };

  if (upgradeDisabled && rendererWindow) {
    rendererWindow.webContents.send('version-info-received', { localVersion });
    return;
  }

  requestLatestRelease();
});

ipcMain.on('get-auth-token', event => {
  keytar.getPassword('LBRY', 'auth_token').then(token => {
    event.sender.send('auth-token-response', token ? token.toString().trim() : null);
  });
});

ipcMain.on('set-auth-token', (event, token) => {
  keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
});

ipcMain.on('delete-auth-token', (event, password) => {
  keytar.deletePassword('LBRY', 'auth_token', password).then(res => {
    event.sender.send('delete-auth-token-response', res);
  });
});

ipcMain.on('get-password', event => {
  keytar.getPassword('LBRY', 'wallet_password').then(password => {
    event.sender.send('get-password-response', password ? password.toString() : null);
  });
});

ipcMain.on('set-password', (event, password) => {
  if (password || password === '') {
    keytar.setPassword('LBRY', 'wallet_password', password).then(res => {
      event.sender.send('set-password-response', res);
    });
  }
});

ipcMain.on('delete-password', event => {
  keytar.deletePassword('LBRY', 'wallet_password').then(res => {
    event.sender.send('delete-password-response', res);
  });
});

process.on('uncaughtException', error => {
  console.log(error);
  dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
  appState.isQuitting = true;
  if (daemon) daemon.quit();
  app.exit(1);
});
<|endoftext|>