<commit_before>//
// browser.js - client-side engine
//

var isFileProtocol = (location.protocol === 'file:' ||
                      location.protocol === 'chrome:' ||
                      location.protocol === 'resource:');

less.env = location.hostname == '127.0.0.1' ||
           location.hostname == '0.0.0.0'   ||
           location.hostname == 'localhost' ||
           location.port.length > 0         ||
           isFileProtocol                   ? 'development'
                                            : 'production';

// Load styles asynchronously (default: false)
//
// This is set to `false` by default, so that the body
// doesn't start loading before the stylesheets are parsed.
// Setting this to `true` can result in flickering.
//
less.async = false;

// Interval between watch polls
less.poll = isFileProtocol ? 1000 : 1500;

//
// Watch mode
//
less.watch   = function () { return this.watchMode = true };
less.unwatch = function () { return this.watchMode = false };

if (less.env === 'development') {
    less.optimization = 0;

    if (/!watch/.test(location.hash)) {
        less.watch();
    }
    less.watchTimer = setInterval(function () {
        if (less.watchMode) {
            loadStyleSheets(function (root, sheet, env) {
                if (root) {
                    createCSS(root.toCSS(), sheet, env.lastModified);
                }
            });
        }
    }, less.poll);
} else {
    less.optimization = 3;
}

var cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;

//
// Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
//
var links = document.getElementsByTagName('link');

less.sheets = [];

for (var i = 0; i < links.length; i++) {
    if (links[i].rel === 'stylesheet/less') {
        less.sheets.push(links[i]);
    }
}

var startTime = endTime = new(Date);

less.refresh = function (reload) {
    loadStyleSheets(function (root, sheet, env) {
        if (env.local) {
            log("loading " + sheet.href + " from cache.");
        } else {
            log("parsed " + sheet.href + " successfully.");
            createCSS(root.toCSS(), sheet, env.lastModified);
        }
        log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms');
        (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms');
        endTime = new(Date);
    }, reload);
};

less.refresh();

function loadStyleSheets(callback, reload) {
    for (var i = 0; i < less.sheets.length; i++) {
        loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1));
    }
}

function loadStyleSheet(sheet, callback, reload, remaining) {
    var href      = sheet.href.replace(/\?.*$/, '');
    var css       = cache && cache.getItem(href);
    var timestamp = cache && cache.getItem(href + ':timestamp');
    var styles    = { css: css, timestamp: timestamp };

    xhr(sheet.href, function (data, lastModified) {
        if (!reload && styles &&
           (new(Date)(lastModified).valueOf() ===
            new(Date)(styles.timestamp).valueOf())) {
            // Use local copy
            createCSS(styles.css, sheet);
            callback(null, sheet, { local: true, remaining: remaining });
        } else {
            // Use remote copy (re-parse)
            new(less.Parser)({
                optimization: less.optimization,
                paths: [href.replace(/[\w\.-]+$/, '')]
            }).parse(data, function (e, root) {
                if (e) { return error(e, href) }
                try {
                    callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining });
                    removeNode(document.getElementById('less-error-message:' + href.replace(/[^a-z]+/gi, '-')));
                } catch (e) {
                    error(e, href);
                }
            });
        }
    }, function (status, url) {
        throw new(Error)("Couldn't load " + url+ " (" + status + ")");
    });
}

function createCSS(styles, sheet, lastModified) {
    var css;

    // Strip the query-string
    var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : '';

    // If there is no title set, use the filename, minus the extension
    var id = 'less:' + (sheet.title || href.match(/(?:^|\/)([-\w]+)\.[a-z]+$/i)[1]);

    // If the stylesheet doesn't exist, create a new node
    if ((css = document.getElementById(id)) === null) {
        css = document.createElement('style');
        css.type = 'text/css';
        css.media = sheet.media;
        css.id = id;
        document.getElementsByTagName('head')[0].appendChild(css);
    }

    if (css.styleSheet) { // IE
        try {
            css.styleSheet.cssText = styles;
        } catch (e) {
            throw new(Error)("Couldn't reassign styleSheet.cssText.");
        }
    } else {
        (function (node) {
            if (css.childNodes.length > 0) {
                if (css.firstChild.nodeValue !== node.nodeValue) {
                    css.replaceChild(node, css.firstChild);
                }
            } else {
                css.appendChild(node);
            }
        })(document.createTextNode(styles));
    }

    // Don't update the local store if the file wasn't modified
    if (lastModified && cache) {
        log('saving ' + href + ' to cache.');
        cache.setItem(href, styles);
        cache.setItem(href + ':timestamp', lastModified);
    }
}

function xhr(url, callback, errback) {
    var xhr = getXMLHttpRequest();
    var async = isFileProtocol ? false : less.async;

    if (typeof xhr.overrideMimeType === 'function') {
      xhr.overrideMimeType('text/css');
    }
    xhr.open('GET', url, async);
    xhr.send(null);

    if (isFileProtocol) {
        if (xhr.status === 0) {
            callback(xhr.responseText);
        } else {
            errback(xhr.status);
        }
    } else if (async) {
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                handleResponse(xhr, callback, errback);
            }
        };
    } else {
        handleResponse(xhr, callback, errback);
    }

    function handleResponse(xhr, callback, errback) {
        if (xhr.status >= 200 && xhr.status < 300) {
            callback(xhr.responseText,
                     xhr.getResponseHeader("Last-Modified"));
        } else if (typeof(errback) === 'function') {
            errback(xhr.status, url);
        }
    }
}

function getXMLHttpRequest() {
    if (window.XMLHttpRequest) {
        return new(XMLHttpRequest);
    } else {
        try {
            return new(ActiveXObject)("MSXML2.XMLHTTP.3.0");
        } catch (e) {
            log("browser doesn't support AJAX.");
            return null;
        }
    }
}

function removeNode(node) {
    return node && node.parentNode.removeChild(node);
}

function log(str) {
    if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) }
}

function error(e, href) {
    var id = 'less-error-message:' + href.replace(/[^a-z]+/ig, '-');

    if (! e.extract) { throw e }

    var template = ['
',
                        '
[-1]{0}
',
                        '
[0]{current}
',
                        '
[1]{2}
',
                    '
'].join('\n');

    var elem = document.createElement('div'), timer;
    elem.id = id;
    elem.className = "less-error-message";
    elem.innerHTML = '
' + (e.message || 'There is an error in your .less file') + '
' +
                     '
' + href + " "                +
                     'on line '     + e.line + ', column ' + (e.column + 1)         + ':
' +
                     template.replace(/\[(-?\d)\]/g, function (_, i) {
                         return (parseInt(e.line) + parseInt(i)) || '';
                     }).replace(/\{(\d)\}/g, function (_, i) {
                         return e.extract[parseInt(i)] || '';
                     }).replace(/\{current\}/, e.extract[1].slice(0, e.column)      +
                                               ''               +
                                               e.extract[1].slice(e.column)         +
                                               '');
    // CSS for error messages
    createCSS([
        '.less-error-message span {',
            'margin-right: 15px;',
        '}',
        '.less-error-message pre {',
            'color: #ee4444;',
            'padding: 4px 0;',
            'margin: 0;',
        '}',
        '.less-error-message pre.ctx {',
            'color: #dd7777;',
        '}',
        '.less-error-message h3 {',
            'padding: 15px 0 5px 0;',
            'margin: 0;',
        '}',
        '.less-error-message a {',
            'color: #10a',
        '}',
        '.less-error-message .error {',
            'color: red;',
            'font-weight: bold;',
            'padding-bottom: 2px;',
            'border-bottom: 1px dashed red;',
        '}'
    ].join('\n'), { title: 'error-message' });

    elem.style.cssText = [
        "font-family: Arial, sans-serif",
        "border: 1px solid #e00",
        "background-color: #eee",
        "border-radius: 5px",
        "-webkit-border-radius: 5px",
        "-moz-border-radius: 5px",
        "color: #e00",
        "padding: 15px",
        "margin-bottom: 15px"
    ].join(';');

    if (less.env == 'development') {
        timer = setInterval(function () {
            if (document.body) {
                if (document.getElementById(id)) {
                    document.body.replaceChild(elem, document.getElementById(id));
                } else {
                    document.body.insertBefore(elem, document.body.firstChild);
                }
                clearInterval(timer);
            }
        }, 10);
    }
}

<commit_msg>bug: window.localStorage in Firefox<commit_after>//
// browser.js - client-side engine
//

var isFileProtocol = (location.protocol === 'file:' ||
                      location.protocol === 'chrome:' ||
                      location.protocol === 'resource:');

less.env = location.hostname == '127.0.0.1' ||
           location.hostname == '0.0.0.0'   ||
           location.hostname == 'localhost' ||
           location.port.length > 0         ||
           isFileProtocol                   ? 'development'
                                            : 'production';

// Load styles asynchronously (default: false)
//
// This is set to `false` by default, so that the body
// doesn't start loading before the stylesheets are parsed.
// Setting this to `true` can result in flickering.
//
less.async = false;

// Interval between watch polls
less.poll = isFileProtocol ? 1000 : 1500;

//
// Watch mode
//
less.watch   = function () { return this.watchMode = true };
less.unwatch = function () { return this.watchMode = false };

if (less.env === 'development') {
    less.optimization = 0;

    if (/!watch/.test(location.hash)) {
        less.watch();
    }
    less.watchTimer = setInterval(function () {
        if (less.watchMode) {
            loadStyleSheets(function (root, sheet, env) {
                if (root) {
                    createCSS(root.toCSS(), sheet, env.lastModified);
                }
            });
        }
    }, less.poll);
} else {
    less.optimization = 3;
}

var cache;
try {
  cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;
} catch (e) {
  cache = null;
}

//
// Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
//
var links = document.getElementsByTagName('link');

less.sheets = [];

for (var i = 0; i < links.length; i++) {
    if (links[i].rel === 'stylesheet/less') {
        less.sheets.push(links[i]);
    }
}

var startTime = endTime = new(Date);

less.refresh = function (reload) {
    loadStyleSheets(function (root, sheet, env) {
        if (env.local) {
            log("loading " + sheet.href + " from cache.");
        } else {
            log("parsed " + sheet.href + " successfully.");
            createCSS(root.toCSS(), sheet, env.lastModified);
        }
        log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms');
        (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms');
        endTime = new(Date);
    }, reload);
};

less.refresh();

function loadStyleSheets(callback, reload) {
    for (var i = 0; i < less.sheets.length; i++) {
        loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1));
    }
}

function loadStyleSheet(sheet, callback, reload, remaining) {
    var href      = sheet.href.replace(/\?.*$/, '');
    var css       = cache && cache.getItem(href);
    var timestamp = cache && cache.getItem(href + ':timestamp');
    var styles    = { css: css, timestamp: timestamp };

    xhr(sheet.href, function (data, lastModified) {
        if (!reload && styles &&
           (new(Date)(lastModified).valueOf() ===
            new(Date)(styles.timestamp).valueOf())) {
            // Use local copy
            createCSS(styles.css, sheet);
            callback(null, sheet, { local: true, remaining: remaining });
        } else {
            // Use remote copy (re-parse)
            new(less.Parser)({
                optimization: less.optimization,
                paths: [href.replace(/[\w\.-]+$/, '')]
            }).parse(data, function (e, root) {
                if (e) { return error(e, href) }
                try {
                    callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining });
                    removeNode(document.getElementById('less-error-message:' + href.replace(/[^a-z]+/gi, '-')));
                } catch (e) {
                    error(e, href);
                }
            });
        }
    }, function (status, url) {
        throw new(Error)("Couldn't load " + url+ " (" + status + ")");
    });
}

function createCSS(styles, sheet, lastModified) {
    var css;

    // Strip the query-string
    var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : '';

    // If there is no title set, use the filename, minus the extension
    var id = 'less:' + (sheet.title || href.match(/(?:^|\/)([-\w]+)\.[a-z]+$/i)[1]);

    // If the stylesheet doesn't exist, create a new node
    if ((css = document.getElementById(id)) === null) {
        css = document.createElement('style');
        css.type = 'text/css';
        css.media = sheet.media;
        css.id = id;
        document.getElementsByTagName('head')[0].appendChild(css);
    }

    if (css.styleSheet) { // IE
        try {
            css.styleSheet.cssText = styles;
        } catch (e) {
            throw new(Error)("Couldn't reassign styleSheet.cssText.");
        }
    } else {
        (function (node) {
            if (css.childNodes.length > 0) {
                if (css.firstChild.nodeValue !== node.nodeValue) {
                    css.replaceChild(node, css.firstChild);
                }
            } else {
                css.appendChild(node);
            }
        })(document.createTextNode(styles));
    }

    // Don't update the local store if the file wasn't modified
    if (lastModified && cache) {
        log('saving ' + href + ' to cache.');
        cache.setItem(href, styles);
        cache.setItem(href + ':timestamp', lastModified);
    }
}

function xhr(url, callback, errback) {
    var xhr = getXMLHttpRequest();
    var async = isFileProtocol ? false : less.async;

    if (typeof xhr.overrideMimeType === 'function') {
      xhr.overrideMimeType('text/css');
    }
    xhr.open('GET', url, async);
    xhr.send(null);

    if (isFileProtocol) {
        if (xhr.status === 0) {
            callback(xhr.responseText);
        } else {
            errback(xhr.status);
        }
    } else if (async) {
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                handleResponse(xhr, callback, errback);
            }
        };
    } else {
        handleResponse(xhr, callback, errback);
    }

    function handleResponse(xhr, callback, errback) {
        if (xhr.status >= 200 && xhr.status < 300) {
            callback(xhr.responseText,
                     xhr.getResponseHeader("Last-Modified"));
        } else if (typeof(errback) === 'function') {
            errback(xhr.status, url);
        }
    }
}

function getXMLHttpRequest() {
    if (window.XMLHttpRequest) {
        return new(XMLHttpRequest);
    } else {
        try {
            return new(ActiveXObject)("MSXML2.XMLHTTP.3.0");
        } catch (e) {
            log("browser doesn't support AJAX.");
            return null;
        }
    }
}

function removeNode(node) {
    return node && node.parentNode.removeChild(node);
}

function log(str) {
    if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) }
}

function error(e, href) {
    var id = 'less-error-message:' + href.replace(/[^a-z]+/ig, '-');

    if (! e.extract) { throw e }

    var template = ['
',
                        '
[-1]{0}
',
                        '
[0]{current}
',
                        '
[1]{2}
',
                    '
'].join('\n');

    var elem = document.createElement('div'), timer;
    elem.id = id;
    elem.className = "less-error-message";
    elem.innerHTML = '
' + (e.message || 'There is an error in your .less file') + '
' +
                     '
' + href + " "                +
                     'on line '     + e.line + ', column ' + (e.column + 1)         + ':
' +
                     template.replace(/\[(-?\d)\]/g, function (_, i) {
                         return (parseInt(e.line) + parseInt(i)) || '';
                     }).replace(/\{(\d)\}/g, function (_, i) {
                         return e.extract[parseInt(i)] || '';
                     }).replace(/\{current\}/, e.extract[1].slice(0, e.column)      +
                                               ''               +
                                               e.extract[1].slice(e.column)         +
                                               '');
    // CSS for error messages
    createCSS([
        '.less-error-message span {',
            'margin-right: 15px;',
        '}',
        '.less-error-message pre {',
            'color: #ee4444;',
            'padding: 4px 0;',
            'margin: 0;',
        '}',
        '.less-error-message pre.ctx {',
            'color: #dd7777;',
        '}',
        '.less-error-message h3 {',
            'padding: 15px 0 5px 0;',
            'margin: 0;',
        '}',
        '.less-error-message a {',
            'color: #10a',
        '}',
        '.less-error-message .error {',
            'color: red;',
            'font-weight: bold;',
            'padding-bottom: 2px;',
            'border-bottom: 1px dashed red;',
        '}'
    ].join('\n'), { title: 'error-message' });

    elem.style.cssText = [
        "font-family: Arial, sans-serif",
        "border: 1px solid #e00",
        "background-color: #eee",
        "border-radius: 5px",
        "-webkit-border-radius: 5px",
        "-moz-border-radius: 5px",
        "color: #e00",
        "padding: 15px",
        "margin-bottom: 15px"
    ].join(';');

    if (less.env == 'development') {
        timer = setInterval(function () {
            if (document.body) {
                if (document.getElementById(id)) {
                    document.body.replaceChild(elem, document.getElementById(id));
                } else {
                    document.body.insertBefore(elem, document.body.firstChild);
                }
                clearInterval(timer);
            }
        }, 10);
    }
}

<|endoftext|>


<commit_before>//
// Select all links with the 'rel' attribute set to "less"
//
var sheets = [];

if (!document.querySelectorAll && typeof(jQuery) === "undefined") {
    log("No selector method found");
} else {
    sheets = (document.querySelectorAll || jQuery).call(document, 'link[rel="stylesheet/less"]');
}

less.env = location.hostname == '127.0.0.1' ||
           location.hostname == '0.0.0.0'   ||
           location.hostname == 'localhost' ||
           location.protocol == 'file:'     ? 'development'
                                            : 'production';

for (var i = 0; i < sheets.length; i++) {
    (function (sheet) { // Because the functions here are async, we need to create a closure
        var css = typeof(localStorage) !== "undefined" && localStorage.getItem(sheet.href);
        var styles = css && JSON.parse(css);

        xhr(sheet.href, function (data, lastModified) {
            if (styles && (new(Date)(lastModified).valueOf() ===
                           new(Date)(styles.timestamp).valueOf())) {
                // Use local copy
                createCSS(styles.css, sheet);
                log("less: loading " + sheet.href + " from local storage.");
            } else {
                // Use remote copy (re-parse)
                new(less.Parser)({ optimization: 3 }).parse(data, function (e, root) {
                    if (e) { return error(e, sheet.href) }
                    createCSS(root.toCSS(), sheet, lastModified);
                    log("less: parsed " + sheet.href + " successfully.");
                });
            }
        });
    })(sheets[i]);
}

function createCSS(styles, sheet, lastModified) {
    var css = document.createElement('style');
    css.type = 'text/css';
    css.media = 'screen';
    css.title = 'less-sheet';

    if (sheet) {
        css.title = sheet.title || sheet.href.match(/(^|\/)([-\w]+)\.[a-z]+$/i)[1];

        // Don't update the local store if the file wasn't modified
        if (lastModified && typeof(localStorage) !== "undefined") {
            localStorage.setItem(sheet.href, JSON.stringify({ timestamp: lastModified, css: styles }));
        }
    }

    if (css.styleSheet) {
        css.styleSheet.cssText = styles;
    } else {
        css.appendChild(document.createTextNode(styles));
    }
    document.getElementsByTagName('head')[0].appendChild(css);
}

function xhr(url, callback, errback) {
    var xhr = new(XMLHttpRequest);

    if (window.location.protocol === "file:") {
        xhr.open('GET', url, false);
        xhr.send(null);
        if (xhr.status === 0) {
            callback(xhr.responseText);
        } else {
            errback(xhr.responseText);
        }
    } else {
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status >= 200 && this.status < 300) {
                    callback(this.responseText,
                             this.getResponseHeader("Last-Modified"));
                } else if (typeof(errback) === 'function') {
                    errback(this.responseText);
                }
            }
        };
        xhr.send(null);
    }
}

function log(str) {
    if (less.env == 'development' && typeof(console) !== "undefined") { console.log(str) }
}

function error(e, href) {
    var template = ['
',
                        '
[-1]{0}
',
                        '
[0]{current}
',
                        '
[1]{2}
',
                    '
'].join('\n');

    var elem = document.createElement('div'), timer;
    elem.id = "less-error-message";
    elem.innerHTML = '
There is an error in your .less file
 '           +
                     '
' + href + " "            +
                     'on line '     + e.line + ', column ' + e.column + ':
' +
                     template.replace(/\[(-?\d)\]/g, function (_, i) {
                         return e.line + parseInt(i);
                     }).replace(/\{(\d)\}/g, function (_, i) {
                         return e.extract[parseInt(i)];
                     }).replace(/\{current\}/, e.extract[1].slice(0, e.column)  +
                                               ''           +
                                               e.extract[1].slice(e.column) +
                                               '');

    createCSS([
        '#less-error-message span {',
            'margin-right: 15px;',
        '}',
        '#less-error-message pre {',
            'color: #ee4444;',
            'padding: 4px 0;',
            'margin: 0;',
        '}',
        '#less-error-message pre.ctx {',
            'color: #ee7777;',
        '}',
        '#less-error-message h3 {',
            'padding: 15px 0 5px 0;',
            'margin: 0;',
        '}',
        '#less-error-message a {',
            'color: #10a',
        '}',
        '#less-error-message .error {',
            'color: red;',
            'font-weight: bold;',
            'padding-bottom: 2px;',
            'border-bottom: 1px dashed red;',
        '}'
    ].join(''));

    elem.style.cssText = [
        "font-family: Arial, sans-serif",
        "border: 1px solid #e00",
        "background-color: #eee",
        "border-radius: 5px",
        "color: #e00",
        "padding: 15px",
        "margin-bottom: 15px"
    ].join(';');

    if (less.env == 'development') {
        timer = setInterval(function () {
            if (document.body) {
                document.body.insertBefore(elem, document.body.childNodes[0]);
                clearInterval(timer);
            }
        }, 10);
    }
}
<commit_msg>Fix for IE6 compatibility<commit_after>//
// Select all links with the 'rel' attribute set to "less"
//
var sheets = [];

if (!document.querySelectorAll && typeof(jQuery) === "undefined") {
    log("No selector method found");
} else {
    sheets = (document.querySelectorAll || jQuery).call(document, 'link[rel="stylesheet/less"]');
}

less.env = location.hostname == '127.0.0.1' ||
           location.hostname == '0.0.0.0'   ||
           location.hostname == 'localhost' ||
           location.protocol == 'file:'     ? 'development'
                                            : 'production';

for (var i = 0; i < sheets.length; i++) {
    (function (sheet) { // Because the functions here are async, we need to create a closure
        var css = typeof(localStorage) !== "undefined" && localStorage.getItem(sheet.href);
        var styles = css && JSON.parse(css);

        xhr(sheet.href, function (data, lastModified) {
            if (styles && (new(Date)(lastModified).valueOf() ===
                           new(Date)(styles.timestamp).valueOf())) {
                // Use local copy
                createCSS(styles.css, sheet);
                log("less: loading " + sheet.href + " from local storage.");
            } else {
                // Use remote copy (re-parse)
                new(less.Parser)({ optimization: 3 }).parse(data, function (e, root) {
                    if (e) { return error(e, sheet.href) }
                    createCSS(root.toCSS(), sheet, lastModified);
                    log("less: parsed " + sheet.href + " successfully.");
                });
            }
        });
    })(sheets[i]);
}

function createCSS(styles, sheet, lastModified) {
    var css = document.createElement('style');
    css.type = 'text/css';
    css.media = 'screen';
    css.title = 'less-sheet';

    if (sheet) {
        css.title = sheet.title || sheet.href.match(/(^|\/)([-\w]+)\.[a-z]+$/i)[1];

        // Don't update the local store if the file wasn't modified
        if (lastModified && typeof(localStorage) !== "undefined") {
            localStorage.setItem(sheet.href, JSON.stringify({ timestamp: lastModified, css: styles }));
        }
    }

    if (css.styleSheet) {
        css.styleSheet.cssText = styles;
    } else {
        css.appendChild(document.createTextNode(styles));
    }
    document.getElementsByTagName('head')[0].appendChild(css);
}

function xhr(url, callback, errback) {
    var xhr = getXMLHttpRequest();

    if (window.location.protocol === "file:") {
        xhr.open('GET', url, false);
        xhr.send(null);
        if (xhr.status === 0) {
            callback(xhr.responseText);
        } else {
            errback(xhr.responseText);
        }
    } else {
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    callback(xhr.responseText,
                             xhr.getResponseHeader("Last-Modified"));
                } else if (typeof(errback) === 'function') {
                    errback(xhr.responseText);
                }
            }
        };
        xhr.send(null);
    }
}

function getXMLHttpRequest() {
    if (window.XMLHttpRequest) {
        return new(XMLHttpRequest);
    } else {
        try {
            return new(ActiveXObject)("MSXML2.XMLHTTP.3.0");
        } catch(ex) {
            return null;
        }
    }
}

function log(str) {
    if (less.env == 'development' && typeof(console) !== "undefined") { console.log(str) }
}

function error(e, href) {
    var template = ['
',
                        '
[-1]{0}
',
                        '
[0]{current}
',
                        '
[1]{2}
',
                    '
'].join('\n');

    var elem = document.createElement('div'), timer;
    elem.id = "less-error-message";
    elem.innerHTML = '
There is an error in your .less file
 '           +
                     '
' + href + " "            +
                     'on line '     + e.line + ', column ' + e.column + ':
' +
                     template.replace(/\[(-?\d)\]/g, function (_, i) {
                         return e.line + parseInt(i);
                     }).replace(/\{(\d)\}/g, function (_, i) {
                         return e.extract[parseInt(i)];
                     }).replace(/\{current\}/, e.extract[1].slice(0, e.column)  +
                                               ''           +
                                               e.extract[1].slice(e.column) +
                                               '');

    createCSS([
        '#less-error-message span {',
            'margin-right: 15px;',
        '}',
        '#less-error-message pre {',
            'color: #ee4444;',
            'padding: 4px 0;',
            'margin: 0;',
        '}',
        '#less-error-message pre.ctx {',
            'color: #ee7777;',
        '}',
        '#less-error-message h3 {',
            'padding: 15px 0 5px 0;',
            'margin: 0;',
        '}',
        '#less-error-message a {',
            'color: #10a',
        '}',
        '#less-error-message .error {',
            'color: red;',
            'font-weight: bold;',
            'padding-bottom: 2px;',
            'border-bottom: 1px dashed red;',
        '}'
    ].join(''));

    elem.style.cssText = [
        "font-family: Arial, sans-serif",
        "border: 1px solid #e00",
        "background-color: #eee",
        "border-radius: 5px",
        "color: #e00",
        "padding: 15px",
        "margin-bottom: 15px"
    ].join(';');

    if (less.env == 'development') {
        timer = setInterval(function () {
            if (document.body) {
                document.body.insertBefore(elem, document.body.childNodes[0]);
                clearInterval(timer);
            }
        }, 10);
    }
}
<|endoftext|>


<commit_before>(function($) {
  $(document).on('rails_admin.dom_ready', function(e, content) {
    var $editors, array, config_options, goBootstrapWysihtml5s, goCkeditors, goCodeMirrors, goFroalaWysiwygs, goSimpleMDEs, options;
    content = content ? content : $('form');
    if (content.length) {
      content.find('[data-color]').each(function() {
        var that;
        that = this;
        return $(this).ColorPicker({
          color: $(that).val(),
          onShow: function(el) {
            $(el).fadeIn(500);
            return false;
          },
          onHide: function(el) {
            $(el).fadeOut(500);
            return false;
          },
          onChange: function(hsb, hex, rgb) {
            $(that).val(hex);
            $(that).css('backgroundColor', '#' + hex);
          }
        });
      });
      $.fn.datetimepicker.defaults.icons = {
        time: 'fa fa-clock-o',
        date: 'fa fa-calendar',
        up: 'fa fa-chevron-up',
        down: 'fa fa-chevron-down',
        previous: 'fa fa-angle-double-left',
        next: 'fa fa-angle-double-right',
        today: 'fa fa-dot-circle-o',
        clear: 'fa fa-trash',
        close: 'fa fa-times'
      };
      content.find('[data-datetimepicker]').each(function() {
        var options;
        options = $(this).data('options');
        $.extend(options, {
          locale: RailsAdmin.I18n.locale
        });
        $(this).datetimepicker(options);
      });
      content.find('[data-enumeration]').each(function() {
        if ($(this).is('[multiple]')) {
          $(this).filteringMultiselect($(this).data('options'));
        } else {
          $(this).filteringSelect($(this).data('options'));
        }
      });
      content.find('[data-fileupload]').each(function() {
        var parent;
        parent = $(this).closest('.controls');
        parent.find('.btn-remove-image').on('click', function() {
          $(this).siblings('[type=checkbox]').click();
          parent.find('.toggle').toggle('slow');
          $(this).toggleClass('btn-danger btn-info');
          return false;
        });
      });
      content.find('[data-fileupload]').change(function() {
        var ext, image_container, input, reader;
        input = this;
        image_container = $("#" + input.id).parent().children(".preview");
        if (!image_container.length) {
          image_container = $("#" + input.id).parent().prepend($('').addClass('preview').addClass('img-thumbnail')).find('img.preview');
          image_container.parent().find('img:not(.preview)').hide();
        }
        ext = $("#" + input.id).val().split('.').pop().toLowerCase();
        if (input.files && input.files[0] && $.inArray(ext, ['gif', 'png', 'jpg', 'jpeg', 'bmp']) !== -1) {
          reader = new FileReader();
          reader.onload = function(e) {
            image_container.attr("src", e.target.result);
          };
          reader.readAsDataURL(input.files[0]);
          image_container.show();
        } else {
          image_container.hide();
        }
      });
      content.find('[data-multiple-fileupload]').each(function() {
        $(this).closest('.controls').find('.btn-remove-image').on('click', function() {
          $(this).siblings('[type=checkbox]').click();
          $(this).parent('.toggle').toggle('slow');
          $(this).toggleClass('btn-danger btn-info');
          return false;
        }).end().sortable({
          items: '.sortables'
        });
      });
      content.find('[data-multiple-fileupload]').change(function() {
        var ext, file, i, image_container, input, len, ref, results;
        input = this;
        $("#" + input.id).parent().children(".preview").remove();
        ref = input.files;
        results = [];
        for (i = 0, len = ref.length; i < len; i++) {
          file = ref[i];
          ext = file.name.split('.').pop().toLowerCase();
          if ($.inArray(ext, ['gif', 'png', 'jpg', 'jpeg', 'bmp']) === -1) {
            continue;
          }
          image_container = $('').addClass('preview').addClass('img-thumbnail');
          results.push((function(image_container) {
            var reader;
            reader = new FileReader();
            reader.onload = function(e) {
              image_container.attr("src", e.target.result);
            };
            reader.readAsDataURL(file);
            return $("#" + input.id).parent().append($('

').addClass('preview').append(image_container));
          })(image_container));
        }
        return results;
      });
      content.find('[data-filteringmultiselect]').each(function() {
        $(this).filteringMultiselect($(this).data('options'));
        if ($(this).parents("#modal").length) {
          $(this).siblings('.btn').remove();
        } else {
          $(this).parents('.control-group').first().remoteForm();
        }
      });
      content.find('[data-filteringselect]').each(function() {
        $(this).filteringSelect($(this).data('options'));
        if ($(this).parents("#modal").length) {
          $(this).siblings('.btn').remove();
        } else {
          $(this).parents('.control-group').first().remoteForm();
        }
      });
      content.find('[data-nestedmany]').each(function() {
        var field, nav, tab_content, toggler;
        field = $(this).parents('.control-group').first();
        nav = field.find('> .controls > .nav');
        tab_content = field.find('> .tab-content');
        toggler = field.find('> .controls > .btn-group > .toggler');
        tab_content.children('.fields:not(.tab-pane)').addClass('tab-pane').each(function() {
          $(this).attr('id', 'unique-id-' + (new Date().getTime()) + Math.floor(Math.random() * 100000));
          nav.append('
' + $(this).children('.object-infos').data('object-label') + '
');
        });
        if (nav.find("> li.active").length === 0) {
          nav.find("> li > a[data-toggle='tab']:first").tab('show');
        }
        if (nav.children().length === 0) {
          nav.hide();
          tab_content.hide();
          toggler.addClass('disabled').removeClass('active').children('i').addClass('icon-chevron-right');
        } else {
          if (toggler.hasClass('active')) {
            nav.show();
            tab_content.show();
            toggler.children('i').addClass('icon-chevron-down');
          } else {
            nav.hide();
            tab_content.hide();
            toggler.children('i').addClass('icon-chevron-right');
          }
        }
      });
      content.find('[data-nestedone]').each(function() {
        var field, first_tab, nav, tab_content, toggler;
        field = $(this).parents('.control-group').first();
        nav = field.find("> .controls > .nav");
        tab_content = field.find("> .tab-content");
        toggler = field.find('> .controls > .btn-group > .toggler');
        tab_content.children(".fields:not(.tab-pane)").addClass('tab-pane active').each(function() {
          field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').html($(this).children('.object-infos').data('object-label'));
          nav.append('
' + $(this).children('.object-infos').data('object-label') + '
');
        });
        first_tab = nav.find("> li > a[data-toggle='tab']:first");
        first_tab.tab('show');
        field.find("> .controls > [data-target]:first").html(' ' + first_tab.html());
        nav.hide();
        if (nav.children().length === 0) {
          nav.hide();
          tab_content.hide();
          toggler.addClass('disabled').removeClass('active').children('i').addClass('icon-chevron-right');
        } else {
          if (toggler.hasClass('active')) {
            toggler.children('i').addClass('icon-chevron-down');
            tab_content.show();
          } else {
            toggler.children('i').addClass('icon-chevron-right');
            tab_content.hide();
          }
        }
      });
      content.find('[data-polymorphic]').each(function() {
        var field, object_select, type_select, urls;
        type_select = $(this);
        field = type_select.parents('.control-group').first();
        object_select = field.find('select').last();
        urls = type_select.data('urls');
        type_select.on('change', function(e) {
          var selected_data, selected_type;
          selected_type = type_select.val().toLowerCase();
          selected_data = $("#" + selected_type + "-js-options").data('options');
          object_select.data('options', selected_data);
          object_select.filteringSelect("destroy");
          object_select.filteringSelect(selected_data);
        });
      });
      goSimpleMDEs = function() {
        return content.find('[data-richtext=simplemde]').not('.simplemded').each(function(index, domEle) {
          var instance_config, options;
          options = $(this).data('options');
          instance_config = options.instance_config;
          new window.SimpleMDE($.extend(true, {
            element: document.getElementById(this.id),
            autosave: {
              uniqueId: this.id
            }
          }, instance_config));
          $(this).addClass('simplemded');
        });
      };
      $editors = content.find('[data-richtext=simplemde]').not('.simplemded');
      if ($editors.length) {
        if (!window.SimpleMDE) {
          options = $editors.first().data('options');
          $('head').append('<link href="' + options['css_location'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['js_location'], function(script, textStatus, jqXHR) {
            return goSimpleMDEs();
          });
        } else {
          goSimpleMDEs();
        }
      }
      goCkeditors = function() {
        return content.find('[data-richtext=ckeditor]').not('.ckeditored').each(function(index, domEle) {
          var instance;
          try {
            if (instance = window.CKEDITOR.instances[this.id]) {
              instance.destroy(true);
            }
          } catch (error1) {}
          window.CKEDITOR.replace(this, $(this).data('options'));
          $(this).addClass('ckeditored');
        });
      };
      $editors = content.find('[data-richtext=ckeditor]').not('.ckeditored');
      if ($editors.length) {
        if (!window.CKEDITOR) {
          options = $editors.first().data('options');
          window.CKEDITOR_BASEPATH = options['base_location'];
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goCkeditors();
            };
          })(this));
        } else {
          goCkeditors();
        }
      }
      goCodeMirrors = (function(_this) {
        return function(array) {
          return array.each(function(index, domEle) {
            var textarea;
            options = $(this).data('options');
            textarea = this;
            $.getScript(options['locations']['mode'], function(script, textStatus, jqXHR) {
              options = $(domEle).data('options');
              $('head').append('<link href="' + options['locations']['theme'] + '" rel="stylesheet" media="all" type="text\/css">');
              CodeMirror.fromTextArea(textarea, options['options']);
              return $(textarea).addClass('codemirrored');
            });
          });
        };
      })(this);
      array = content.find('[data-richtext=codemirror]').not('.codemirrored');
      if (array.length) {
        this.array = array;
        if (!window.CodeMirror) {
          options = $(array[0]).data('options');
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goCodeMirrors(_this.array);
            };
          })(this));
        } else {
          goCodeMirrors(this.array);
        }
      }
      goBootstrapWysihtml5s = (function(_this) {
        return function(array, config_options) {
          return array.each(function() {
            $(this).addClass('bootstrap-wysihtml5ed');
            $(this).closest('.controls').addClass('well');
            $(this).wysihtml5(config_options);
          });
        };
      })(this);
      array = content.find('[data-richtext=bootstrap-wysihtml5]').not('.bootstrap-wysihtml5ed');
      if (array.length) {
        this.array = array;
        options = $(array[0]).data('options');
        config_options = $.parseJSON(options['config_options']);
        if (!window.wysihtml5) {
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goBootstrapWysihtml5s(_this.array, config_options);
            };
          })(this));
        } else {
          goBootstrapWysihtml5s(this.array, config_options);
        }
      }
      goFroalaWysiwygs = (function(_this) {
        return function(array) {
          return array.each(function() {
            var uploadEnabled;
            options = $(this).data('options');
            config_options = $.parseJSON(options['config_options']);
            if (config_options) {
              if (!config_options['inlineMode']) {
                config_options['inlineMode'] = false;
              }
            } else {
              config_options = {
                inlineMode: false
              };
            }
            uploadEnabled = config_options['imageUploadURL'] ? config_options['imageUploadParams'] = {
              authenticity_token: $('meta[name=csrf-token]').attr('content')
            } : void 0;
            $(this).addClass('froala-wysiwyged');
            $(this).froalaEditor(config_options);
            if (uploadEnabled) {
              $(this).on('froalaEditor.image.error', function(e, editor, error) {
                alert("error uploading image: " + error.message);
              }).on('froalaEditor.image.removed', function(e, editor, $img) {
                editor.options.imageDeleteParams = {
                  src: $img.attr('src'),
                  authenticity_token: $('meta[name=csrf-token]').attr('content')
                };
                editor.deleteImage($img);
              }).on('editable.imageDeleteSuccess', function(e, editor, data) {}).on('editable.imageDeleteError', function(e, editor, error) {
                alert("error deleting image: " + error.message);
              });
            }
          });
        };
      })(this);
      array = content.find('[data-richtext=froala-wysiwyg]').not('.froala-wysiwyged');
      if (array.length) {
        options = $(array[0]).data('options');
        if (!$.isFunction($.fn.editable)) {
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goFroalaWysiwygs(array);
            };
          })(this));
        } else {
          goFroalaWysiwygs(array);
        }
      }
      return content.find('trix-editor').each(function() {
        if (!window.Trix) {
          options = $(this).data('options');
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          return $.getScript(options['jspath']);
        }
      });
    }
  });

}(jQuery));
<commit_msg>Fix failing to pass config location to CKEditor. Fixes #3162<commit_after>(function($) {
  $(document).on('rails_admin.dom_ready', function(e, content) {
    var $editors, array, config_options, goBootstrapWysihtml5s, goCkeditors, goCodeMirrors, goFroalaWysiwygs, goSimpleMDEs, options;
    content = content ? content : $('form');
    if (content.length) {
      content.find('[data-color]').each(function() {
        var that;
        that = this;
        return $(this).ColorPicker({
          color: $(that).val(),
          onShow: function(el) {
            $(el).fadeIn(500);
            return false;
          },
          onHide: function(el) {
            $(el).fadeOut(500);
            return false;
          },
          onChange: function(hsb, hex, rgb) {
            $(that).val(hex);
            $(that).css('backgroundColor', '#' + hex);
          }
        });
      });
      $.fn.datetimepicker.defaults.icons = {
        time: 'fa fa-clock-o',
        date: 'fa fa-calendar',
        up: 'fa fa-chevron-up',
        down: 'fa fa-chevron-down',
        previous: 'fa fa-angle-double-left',
        next: 'fa fa-angle-double-right',
        today: 'fa fa-dot-circle-o',
        clear: 'fa fa-trash',
        close: 'fa fa-times'
      };
      content.find('[data-datetimepicker]').each(function() {
        var options;
        options = $(this).data('options');
        $.extend(options, {
          locale: RailsAdmin.I18n.locale
        });
        $(this).datetimepicker(options);
      });
      content.find('[data-enumeration]').each(function() {
        if ($(this).is('[multiple]')) {
          $(this).filteringMultiselect($(this).data('options'));
        } else {
          $(this).filteringSelect($(this).data('options'));
        }
      });
      content.find('[data-fileupload]').each(function() {
        var parent;
        parent = $(this).closest('.controls');
        parent.find('.btn-remove-image').on('click', function() {
          $(this).siblings('[type=checkbox]').click();
          parent.find('.toggle').toggle('slow');
          $(this).toggleClass('btn-danger btn-info');
          return false;
        });
      });
      content.find('[data-fileupload]').change(function() {
        var ext, image_container, input, reader;
        input = this;
        image_container = $("#" + input.id).parent().children(".preview");
        if (!image_container.length) {
          image_container = $("#" + input.id).parent().prepend($('').addClass('preview').addClass('img-thumbnail')).find('img.preview');
          image_container.parent().find('img:not(.preview)').hide();
        }
        ext = $("#" + input.id).val().split('.').pop().toLowerCase();
        if (input.files && input.files[0] && $.inArray(ext, ['gif', 'png', 'jpg', 'jpeg', 'bmp']) !== -1) {
          reader = new FileReader();
          reader.onload = function(e) {
            image_container.attr("src", e.target.result);
          };
          reader.readAsDataURL(input.files[0]);
          image_container.show();
        } else {
          image_container.hide();
        }
      });
      content.find('[data-multiple-fileupload]').each(function() {
        $(this).closest('.controls').find('.btn-remove-image').on('click', function() {
          $(this).siblings('[type=checkbox]').click();
          $(this).parent('.toggle').toggle('slow');
          $(this).toggleClass('btn-danger btn-info');
          return false;
        }).end().sortable({
          items: '.sortables'
        });
      });
      content.find('[data-multiple-fileupload]').change(function() {
        var ext, file, i, image_container, input, len, ref, results;
        input = this;
        $("#" + input.id).parent().children(".preview").remove();
        ref = input.files;
        results = [];
        for (i = 0, len = ref.length; i < len; i++) {
          file = ref[i];
          ext = file.name.split('.').pop().toLowerCase();
          if ($.inArray(ext, ['gif', 'png', 'jpg', 'jpeg', 'bmp']) === -1) {
            continue;
          }
          image_container = $('').addClass('preview').addClass('img-thumbnail');
          results.push((function(image_container) {
            var reader;
            reader = new FileReader();
            reader.onload = function(e) {
              image_container.attr("src", e.target.result);
            };
            reader.readAsDataURL(file);
            return $("#" + input.id).parent().append($('

').addClass('preview').append(image_container));
          })(image_container));
        }
        return results;
      });
      content.find('[data-filteringmultiselect]').each(function() {
        $(this).filteringMultiselect($(this).data('options'));
        if ($(this).parents("#modal").length) {
          $(this).siblings('.btn').remove();
        } else {
          $(this).parents('.control-group').first().remoteForm();
        }
      });
      content.find('[data-filteringselect]').each(function() {
        $(this).filteringSelect($(this).data('options'));
        if ($(this).parents("#modal").length) {
          $(this).siblings('.btn').remove();
        } else {
          $(this).parents('.control-group').first().remoteForm();
        }
      });
      content.find('[data-nestedmany]').each(function() {
        var field, nav, tab_content, toggler;
        field = $(this).parents('.control-group').first();
        nav = field.find('> .controls > .nav');
        tab_content = field.find('> .tab-content');
        toggler = field.find('> .controls > .btn-group > .toggler');
        tab_content.children('.fields:not(.tab-pane)').addClass('tab-pane').each(function() {
          $(this).attr('id', 'unique-id-' + (new Date().getTime()) + Math.floor(Math.random() * 100000));
          nav.append('
' + $(this).children('.object-infos').data('object-label') + '
');
        });
        if (nav.find("> li.active").length === 0) {
          nav.find("> li > a[data-toggle='tab']:first").tab('show');
        }
        if (nav.children().length === 0) {
          nav.hide();
          tab_content.hide();
          toggler.addClass('disabled').removeClass('active').children('i').addClass('icon-chevron-right');
        } else {
          if (toggler.hasClass('active')) {
            nav.show();
            tab_content.show();
            toggler.children('i').addClass('icon-chevron-down');
          } else {
            nav.hide();
            tab_content.hide();
            toggler.children('i').addClass('icon-chevron-right');
          }
        }
      });
      content.find('[data-nestedone]').each(function() {
        var field, first_tab, nav, tab_content, toggler;
        field = $(this).parents('.control-group').first();
        nav = field.find("> .controls > .nav");
        tab_content = field.find("> .tab-content");
        toggler = field.find('> .controls > .btn-group > .toggler');
        tab_content.children(".fields:not(.tab-pane)").addClass('tab-pane active').each(function() {
          field.find('> .controls .add_nested_fields').removeClass('add_nested_fields').html($(this).children('.object-infos').data('object-label'));
          nav.append('
' + $(this).children('.object-infos').data('object-label') + '
');
        });
        first_tab = nav.find("> li > a[data-toggle='tab']:first");
        first_tab.tab('show');
        field.find("> .controls > [data-target]:first").html(' ' + first_tab.html());
        nav.hide();
        if (nav.children().length === 0) {
          nav.hide();
          tab_content.hide();
          toggler.addClass('disabled').removeClass('active').children('i').addClass('icon-chevron-right');
        } else {
          if (toggler.hasClass('active')) {
            toggler.children('i').addClass('icon-chevron-down');
            tab_content.show();
          } else {
            toggler.children('i').addClass('icon-chevron-right');
            tab_content.hide();
          }
        }
      });
      content.find('[data-polymorphic]').each(function() {
        var field, object_select, type_select, urls;
        type_select = $(this);
        field = type_select.parents('.control-group').first();
        object_select = field.find('select').last();
        urls = type_select.data('urls');
        type_select.on('change', function(e) {
          var selected_data, selected_type;
          selected_type = type_select.val().toLowerCase();
          selected_data = $("#" + selected_type + "-js-options").data('options');
          object_select.data('options', selected_data);
          object_select.filteringSelect("destroy");
          object_select.filteringSelect(selected_data);
        });
      });
      goSimpleMDEs = function() {
        return content.find('[data-richtext=simplemde]').not('.simplemded').each(function(index, domEle) {
          var instance_config, options;
          options = $(this).data('options');
          instance_config = options.instance_config;
          new window.SimpleMDE($.extend(true, {
            element: document.getElementById(this.id),
            autosave: {
              uniqueId: this.id
            }
          }, instance_config));
          $(this).addClass('simplemded');
        });
      };
      $editors = content.find('[data-richtext=simplemde]').not('.simplemded');
      if ($editors.length) {
        if (!window.SimpleMDE) {
          options = $editors.first().data('options');
          $('head').append('<link href="' + options['css_location'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['js_location'], function(script, textStatus, jqXHR) {
            return goSimpleMDEs();
          });
        } else {
          goSimpleMDEs();
        }
      }
      goCkeditors = function() {
        return content.find('[data-richtext=ckeditor]').not('.ckeditored').each(function(index, domEle) {
          var instance;
          try {
            if (instance = window.CKEDITOR.instances[this.id]) {
              instance.destroy(true);
            }
          } catch (error1) {}
          window.CKEDITOR.replace(this, $(this).data('options').options);
          $(this).addClass('ckeditored');
        });
      };
      $editors = content.find('[data-richtext=ckeditor]').not('.ckeditored');
      if ($editors.length) {
        if (!window.CKEDITOR) {
          options = $editors.first().data('options');
          window.CKEDITOR_BASEPATH = options['base_location'];
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goCkeditors();
            };
          })(this));
        } else {
          goCkeditors();
        }
      }
      goCodeMirrors = (function(_this) {
        return function(array) {
          return array.each(function(index, domEle) {
            var textarea;
            options = $(this).data('options');
            textarea = this;
            $.getScript(options['locations']['mode'], function(script, textStatus, jqXHR) {
              options = $(domEle).data('options');
              $('head').append('<link href="' + options['locations']['theme'] + '" rel="stylesheet" media="all" type="text\/css">');
              CodeMirror.fromTextArea(textarea, options['options']);
              return $(textarea).addClass('codemirrored');
            });
          });
        };
      })(this);
      array = content.find('[data-richtext=codemirror]').not('.codemirrored');
      if (array.length) {
        this.array = array;
        if (!window.CodeMirror) {
          options = $(array[0]).data('options');
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goCodeMirrors(_this.array);
            };
          })(this));
        } else {
          goCodeMirrors(this.array);
        }
      }
      goBootstrapWysihtml5s = (function(_this) {
        return function(array, config_options) {
          return array.each(function() {
            $(this).addClass('bootstrap-wysihtml5ed');
            $(this).closest('.controls').addClass('well');
            $(this).wysihtml5(config_options);
          });
        };
      })(this);
      array = content.find('[data-richtext=bootstrap-wysihtml5]').not('.bootstrap-wysihtml5ed');
      if (array.length) {
        this.array = array;
        options = $(array[0]).data('options');
        config_options = $.parseJSON(options['config_options']);
        if (!window.wysihtml5) {
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goBootstrapWysihtml5s(_this.array, config_options);
            };
          })(this));
        } else {
          goBootstrapWysihtml5s(this.array, config_options);
        }
      }
      goFroalaWysiwygs = (function(_this) {
        return function(array) {
          return array.each(function() {
            var uploadEnabled;
            options = $(this).data('options');
            config_options = $.parseJSON(options['config_options']);
            if (config_options) {
              if (!config_options['inlineMode']) {
                config_options['inlineMode'] = false;
              }
            } else {
              config_options = {
                inlineMode: false
              };
            }
            uploadEnabled = config_options['imageUploadURL'] ? config_options['imageUploadParams'] = {
              authenticity_token: $('meta[name=csrf-token]').attr('content')
            } : void 0;
            $(this).addClass('froala-wysiwyged');
            $(this).froalaEditor(config_options);
            if (uploadEnabled) {
              $(this).on('froalaEditor.image.error', function(e, editor, error) {
                alert("error uploading image: " + error.message);
              }).on('froalaEditor.image.removed', function(e, editor, $img) {
                editor.options.imageDeleteParams = {
                  src: $img.attr('src'),
                  authenticity_token: $('meta[name=csrf-token]').attr('content')
                };
                editor.deleteImage($img);
              }).on('editable.imageDeleteSuccess', function(e, editor, data) {}).on('editable.imageDeleteError', function(e, editor, error) {
                alert("error deleting image: " + error.message);
              });
            }
          });
        };
      })(this);
      array = content.find('[data-richtext=froala-wysiwyg]').not('.froala-wysiwyged');
      if (array.length) {
        options = $(array[0]).data('options');
        if (!$.isFunction($.fn.editable)) {
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          $.getScript(options['jspath'], (function(_this) {
            return function(script, textStatus, jqXHR) {
              return goFroalaWysiwygs(array);
            };
          })(this));
        } else {
          goFroalaWysiwygs(array);
        }
      }
      return content.find('trix-editor').each(function() {
        if (!window.Trix) {
          options = $(this).data('options');
          $('head').append('<link href="' + options['csspath'] + '" rel="stylesheet" media="all" type="text\/css">');
          return $.getScript(options['jspath']);
        }
      });
    }
  });

}(jQuery));
<|endoftext|>


<commit_before>import Component from '@ember/component';
import Object, { computed } from '@ember/object';
import { A } from '@ember/array';
import { inject as service } from '@ember/service';

const O = Object.create.bind(Object);

export default Component.extend({
    userAuth: service(),
    folderNavs: service(),
    store: service(),

    catalogTitle: 'WholeTale Catalog',
    catalogParentType: 'collection',
    catalogPath: '/collection/WholeTale Catalog/WholeTale Catalog',
    catalogId: '',
    catalogParentId: '',

    selectedMenuIndex: 0,
    dataSources: A([
        O({ name: 'WholeTale Catalog' }),
        //O({ name: 'My Data' })
    ]),
    selectedDataSource: O({}),

    allSelectedItems: A(),

    folders: A(),
    files: A(),

    loading: true,
    loadingMessage: 'Preparing Files',
    loadError: false,
    currentFolder: null,
    rootFolderId: null,

    model: O({}),
    classNameBindings: ['injectedClassName'],

    injectedClassName: computed('modelType', 'model.{_modelType,_id}', function () {
        if (this.get('modelType')) {
            let newClass = `select-data-modal-${this.get('modelType')}`;
            return newClass;
        } else return '';
    }),

    actions: {
        selectDatasource(datasource, index) {
            this.set('selectedDataSource', datasource);
            this.set('selectedMenuIndex', index);
        },

        initData() {
            this.selectedDataSource = this.get('dataSources')[this.get('selectedMenuIndex')];

            let text = this.get('catalogTitle');
            this.get('store').query('collection', { text }).then((results) => {
                let firstResult = results.firstObject;
                this.set('catalogParentType', firstResult['_modelType']);
                this.set('catalogParentId', firstResult['_id']);

                // Only init after we've located the catalog
                this.initData.call(this);
            });
        },

        updateSessionData() {
            this.updateSessionData.call(this, this.get('allSelectedItems'));
        },

        cancel() {
            this.cancel.call(this);
        },

        dblClick(target) {
            this.dblClick.call(this, target);
        },

        onClick(target) {
            this.onClick.call(this, target);
        },

        goBack() {
            this.goBack.call(this, this.get('currentFolder'));
        },

        addSelectedData() {
            this.addSelectedData.call(this, this.get('files'), this.get('folders'));
        },

        removeSelectedData() {
            this.removeSelectedData.call(this);
        },

        close() {
          const deselect = f => {
            if (f.selected) {
              f.set('selected', false);
            }
          }
          this.allSelectedItems.forEach(deselect);
          this.set('folders', A());
          this.set('files', A());
          this.set('currentFolder', null);
          this.set('rootFolderId', null);
        }
    },

    // -----------------------------------------------------------------------
    // BELOW ARE FUNCTIONS FOR DEFAULT BEHAVIOR:
    //    You can set custom behavior by overriding these functions.
    // -----------------------------------------------------------------------

    initData() {
        this.set('loading', true);
        this.set('loadError', false);
        this.set('loadingMessage', 'Preparing Files');

        // this.set('allSelectedItems', A());
        this.set('folders', A());
        this.set('files', A());
        this.set('currentFolder', null);
        this.set('rootFolderId', null);

        const store = this.get('store');
        let selectedDS = this.get('selectedDataSource');
        let catalogTitle = this.get('catalogTitle');
        let parentId = selectedDS['name'] == catalogTitle ? this.get('catalogParentId') : this.get('userAuth').getCurrentUserID();
        let dataNavInfo = this.get('folderNavs').getFolderNavFor('user_data');
        let parentType = selectedDS['name'] == catalogTitle ? this.get('catalogParentType') : dataNavInfo.parentType;
        let adapterOptions = { queryParams: { limit: "0" } };

        const self = this;
        return store.query('dataset', { adapterOptions }).then(datasets => {
            let catalogId = parentId;
            let parentCollection = parentType;
            self.set('rootFolderId', catalogId);
            self.set('loading', false);
            self.set('datasets', A(datasets));
            self.set('folders', A([]));
            self.set('files', A([]));
            self.set('currentFolder', O({ id: catalogId, _modelType: 'folder', parentCollection, parentId }));

            //return self.loadFolder.call(self, catalogId, 'folder');
        }).catch(e => {
            self.set('loadError', true);
            self.set('loadingMessage', 'Failed to load registered data. Please try again');
        });
    },

    dblClick(target) {
        // HACK: Standard endpoints use "_modelType", /dataset uses "modelType"
        if (!target._modelType && target.modelType) {
            target._modelType = target.modelType;
        }

        if (!target || !target._modelType || (target._modelType !== 'folder' && target._modelType !== 'dataset')) {
            throw new Error('[select-data-modal] Cannot open. Not a folder or dataset.');
        }

        this.set('currentFolder', target);

        const self = this;
        return this.loadFolder.call(this, target.get('id'), target.get('_modelType')).catch(e => {
            self.set('loadError', true);
            self.set('loadingMessage', 'Failed to load registered data. Please try again');
        });
    },

    onClick(target) {
        let selected = !target.get('selected');
        target.set('selected', selected);
    },

    goBack(currentFolder, adapterOptions = { queryParams: { limit: "0" } }) {
        this.set('loading', true);
        this.set('loadError', false);
        this.set('loadingMessage', 'Preparing Files');

        const store = this.get('store');

        let parentId = currentFolder.parentId;
        let parentType = currentFolder.parentCollection;

        const self = this;
        if (parentId == null || parentId == this.get('rootFolderId')) {
            return store.query('dataset', adapterOptions).then(datasets => {
                let catalogId = this.get('rootFolderId');
                self.set('loading', false);
                self.set('currentFolder', O({ id: catalogId, _modelType: 'folder', parentType, catalogId }));
                self.set('datasets', A(datasets));
                self.set('folders', A([]));
                self.set('files', A([]));
            });
        } else {
            return store.find('folder', parentId).then(parent => {
                self.set('currentFolder', parent);
                return self.loadFolder.call(self, parentId, parentType);
            }).catch(e => {
                self.set('loadError', true);
                self.set('loadingMessage', 'Failed to load registered data. Please try again');
            });
        }
    },

    loadFolder(parentId, parentType, adapterOptions = { queryParams: { limit: "0" } }) {
        this.set('loading', true);
        this.set('loadError', false);
        this.set('loadingMessage', 'Preparing Files');

        const store = this.get('store');
        let fetchFolders = store.query('folder', { parentId, parentType, adapterOptions });
        let fetchFiles = store.query('item', { folderId: parentId, adapterOptions });

        const self = this;
        return Promise.all([fetchFolders, fetchFiles]).then(([folders, files]) => {
            self.set('datasets', A([]));
            self.set('folders', A(folders));
            self.set('files', A(files));
            self.set('loading', false);
        });
    },

    addSelectedData(files, folders) {
        const self = this;
        const add = f => {
          if (f.selected) {
            f.set('selected', false);
            let { id, name, _modelType } = f;
            if (!self.allSelectedItems.findBy('id', id)) {
                    self.allSelectedItems.pushObject(O({ id, name, _modelType }));
                }
            }
        }
        files.forEach(add);
        folders.forEach(add);
    },

    removeSelectedData() {
        const self = this;
        const del = f => {
            if (f.selected) {
                self.allSelectedItems.removeObject(f);
            }
        }
        let all = A(this.get('allSelectedItems').concat([]));
        all.forEach(del);
    },

    loadDataset(adapterOptions = { queryParams: { limit: "0" } }) {

    },

    loadTaleWorkspaces(adapterOptions = { queryParams: { limit: "0" } }) {

    },

    updateSessionData() {
        throw new Error('[select-data-modal] "updateSessionData" must be provided!!');
    },

    cancel() {

        // this.set('allSelectedItems', A());

        // NOTE(Adam): This causes an infinite loop where the function calls itself recursively forever.

        // if (this.get('cancel')) {
        //     this.get('cancel')();
        // } else {
        //     throw new Error('[select-data-modal] "cancel" function must be provided!!');
        // }
    }
});
<commit_msg>Make sure datasets are selectable on the select-data-modal<commit_after>import Component from '@ember/component';
import Object, { computed } from '@ember/object';
import { A } from '@ember/array';
import { inject as service } from '@ember/service';

const O = Object.create.bind(Object);

export default Component.extend({
    userAuth: service(),
    folderNavs: service(),
    store: service(),

    catalogTitle: 'WholeTale Catalog',
    catalogParentType: 'collection',
    catalogPath: '/collection/WholeTale Catalog/WholeTale Catalog',
    catalogId: '',
    catalogParentId: '',

    selectedMenuIndex: 0,
    dataSources: A([
        O({ name: 'WholeTale Catalog' }),
        //O({ name: 'My Data' })
    ]),
    selectedDataSource: O({}),

    allSelectedItems: A(),

    folders: A(),
    files: A(),

    loading: true,
    loadingMessage: 'Preparing Files',
    loadError: false,
    currentFolder: null,
    rootFolderId: null,

    model: O({}),
    classNameBindings: ['injectedClassName'],

    injectedClassName: computed('modelType', 'model.{_modelType,_id}', function () {
        if (this.get('modelType')) {
            let newClass = `select-data-modal-${this.get('modelType')}`;
            return newClass;
        } else return '';
    }),

    actions: {
        selectDatasource(datasource, index) {
            this.set('selectedDataSource', datasource);
            this.set('selectedMenuIndex', index);
        },

        initData() {
            this.selectedDataSource = this.get('dataSources')[this.get('selectedMenuIndex')];

            let text = this.get('catalogTitle');
            this.get('store').query('collection', { text }).then((results) => {
                let firstResult = results.firstObject;
                this.set('catalogParentType', firstResult['_modelType']);
                this.set('catalogParentId', firstResult['_id']);

                // Only init after we've located the catalog
                this.initData.call(this);
            });
        },

        updateSessionData() {
            this.updateSessionData.call(this, this.get('allSelectedItems'));
        },

        cancel() {
            this.cancel.call(this);
        },

        dblClick(target) {
            this.dblClick.call(this, target);
        },

        onClick(target) {
            this.onClick.call(this, target);
        },

        goBack() {
            this.goBack.call(this, this.get('currentFolder'));
        },

        addSelectedData() {
            this.addSelectedData.call(this, this.get('datasets'), this.get('files'), this.get('folders'));
        },

        removeSelectedData() {
            this.removeSelectedData.call(this);
        },

        close() {
          const deselect = f => {
            if (f.selected) {
              f.set('selected', false);
            }
          }
          this.allSelectedItems.forEach(deselect);
          this.set('folders', A());
          this.set('files', A());
          this.set('currentFolder', null);
          this.set('rootFolderId', null);
        }
    },

    // -----------------------------------------------------------------------
    // BELOW ARE FUNCTIONS FOR DEFAULT BEHAVIOR:
    //    You can set custom behavior by overriding these functions.
    // -----------------------------------------------------------------------

    initData() {
        this.set('loading', true);
        this.set('loadError', false);
        this.set('loadingMessage', 'Preparing Files');

        // this.set('allSelectedItems', A());
        this.set('folders', A());
        this.set('files', A());
        this.set('currentFolder', null);
        this.set('rootFolderId', null);

        const store = this.get('store');
        let selectedDS = this.get('selectedDataSource');
        let catalogTitle = this.get('catalogTitle');
        let parentId = selectedDS['name'] == catalogTitle ? this.get('catalogParentId') : this.get('userAuth').getCurrentUserID();
        let dataNavInfo = this.get('folderNavs').getFolderNavFor('user_data');
        let parentType = selectedDS['name'] == catalogTitle ? this.get('catalogParentType') : dataNavInfo.parentType;
        let adapterOptions = { queryParams: { limit: "0" } };

        const self = this;
        return store.query('dataset', { adapterOptions }).then(datasets => {
            let catalogId = parentId;
            let parentCollection = parentType;
            self.set('rootFolderId', catalogId);
            self.set('loading', false);
            self.set('datasets', A(datasets));
            self.set('folders', A([]));
            self.set('files', A([]));
            self.set('currentFolder', O({ id: catalogId, _modelType: 'folder', parentCollection, parentId }));

            //return self.loadFolder.call(self, catalogId, 'folder');
        }).catch(e => {
            self.set('loadError', true);
            self.set('loadingMessage', 'Failed to load registered data. Please try again');
        });
    },

    dblClick(target) {
        // HACK: Standard endpoints use "_modelType", /dataset uses "modelType"
        if (!target._modelType && target.modelType) {
            target._modelType = target.modelType;
        }

        if (!target || !target._modelType || (target._modelType !== 'folder' && target._modelType !== 'dataset')) {
            throw new Error('[select-data-modal] Cannot open. Not a folder or dataset.');
        }

        this.set('currentFolder', target);

        const self = this;
        return this.loadFolder.call(this, target.get('id'), target.get('_modelType')).catch(e => {
            self.set('loadError', true);
            self.set('loadingMessage', 'Failed to load registered data. Please try again');
        });
    },

    onClick(target) {
        let selected = !target.get('selected');
        target.set('selected', selected);
    },

    goBack(currentFolder, adapterOptions = { queryParams: { limit: "0" } }) {
        this.set('loading', true);
        this.set('loadError', false);
        this.set('loadingMessage', 'Preparing Files');

        const store = this.get('store');

        let parentId = currentFolder.parentId;
        let parentType = currentFolder.parentCollection;

        const self = this;
        if (parentId == null || parentId == this.get('rootFolderId')) {
            return store.query('dataset', adapterOptions).then(datasets => {
                let catalogId = this.get('rootFolderId');
                self.set('loading', false);
                self.set('currentFolder', O({ id: catalogId, _modelType: 'folder', parentType, catalogId }));
                self.set('datasets', A(datasets));
                self.set('folders', A([]));
                self.set('files', A([]));
            });
        } else {
            return store.find('folder', parentId).then(parent => {
                self.set('currentFolder', parent);
                return self.loadFolder.call(self, parentId, parentType);
            }).catch(e => {
                self.set('loadError', true);
                self.set('loadingMessage', 'Failed to load registered data. Please try again');
            });
        }
    },

    loadFolder(parentId, parentType, adapterOptions = { queryParams: { limit: "0" } }) {
        this.set('loading', true);
        this.set('loadError', false);
        this.set('loadingMessage', 'Preparing Files');

        const store = this.get('store');
        let fetchFolders = store.query('folder', { parentId, parentType, adapterOptions });
        let fetchFiles = store.query('item', { folderId: parentId, adapterOptions });

        const self = this;
        return Promise.all([fetchFolders, fetchFiles]).then(([folders, files]) => {
            self.set('datasets', A([]));
            self.set('folders', A(folders));
            self.set('files', A(files));
            self.set('loading', false);
        });
    },

    addSelectedData(datasets, files, folders) {
        const self = this;
        const add = f => {
          if (f.selected) {
            f.set('selected', false);
            let { id, name, _modelType } = f;
            if (!self.allSelectedItems.findBy('id', id)) {
                    self.allSelectedItems.pushObject(O({ id, name, _modelType }));
                }
            }
        }
        datasets.forEach(add);
        files.forEach(add);
        folders.forEach(add);
    },

    removeSelectedData() {
        const self = this;
        const del = f => {
            if (f.selected) {
                self.allSelectedItems.removeObject(f);
            }
        }
        let all = A(this.get('allSelectedItems').concat([]));
        all.forEach(del);
    },

    loadDataset(adapterOptions = { queryParams: { limit: "0" } }) {

    },

    loadTaleWorkspaces(adapterOptions = { queryParams: { limit: "0" } }) {

    },

    updateSessionData() {
        throw new Error('[select-data-modal] "updateSessionData" must be provided!!');
    },

    cancel() {

        // this.set('allSelectedItems', A());

        // NOTE(Adam): This causes an infinite loop where the function calls itself recursively forever.

        // if (this.get('cancel')) {
        //     this.get('cancel')();
        // } else {
        //     throw new Error('[select-data-modal] "cancel" function must be provided!!');
        // }
    }
});
<|endoftext|>


<commit_before>import Component from '@ember/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { A } from '@ember/array';

import $ from 'jquery';

import config from '../../../config/environment';

const taleStatus = Object.create({
  NONE: -1,
  READ: 0,
  WRITE: 1,
  ADMIN: 2,
  SITE_ADMIN: 100
});

export default Component.extend({
  store: service(),
  apiHost: config.apiHost,
  environments: [],
  licenses: [],
  model: null,
  // Array of author dicts that the template uses to render rows
  taleAuthors: A(),
  // An error message that can get set, displayed in the error modal
  errorMessage: String(),
  model: null,
  // Flag that can be used to tell if the current user has permission to edit the Tale
  canEditTale: computed('model._accessLevel', function () {
    return this.get('model') && this.get('model') && this.get('model').get('_accessLevel') >= taleStatus.WRITE;
  }).readOnly(),
  cannotEditTale: computed.not('canEditTale').readOnly(),

  publishedURL: computed('model', 'model.publishInfo', function() {
    const tale = this.get('model');
    if (tale.publishInfo && tale.publishInfo.length) {
       return tale.publishInfo[tale.publishInfo.length - 1].uri;
    }
    return null;
  }),

  init() {
    this._super(...arguments);

    // Fetch images for user to select an environment
    const component = this;
    $.getJSON(this.get('apiHost') + '/api/v1/image/').then(function(images) {
      component.set('environments', images);
      component.selectDefaultImageId();
    });

    // Fetch licenses for users to select
    $.getJSON(this.get('apiHost') + '/api/v1/license/').then(function(licenses) {
      component.set('licenses', licenses);
      component.selectDefaultLicense();
    });

    this.refreshAuthors();
  },

  didRender() {
    // Enable dropdown functionality
    $('.ui.dropdown.image').dropdown();
    this.selectDefaultImageId();
    $('.ui.dropdown.license').dropdown();
    this.selectDefaultLicense();
  },

  didUpdateAttrs() {
    this._super(...arguments);
    this.refreshAuthors();
  },

  selectDefaultImageId() {
    // Select the current imageId by default
    const selectedImageId = this.get('model').get('imageId');
    $('.ui.dropdown.image').dropdown('set selected', selectedImageId);
  },

  selectDefaultLicense() {
    // Select the license that the Tale currently has
    const selectedLicense = this.get('model').get('licenseSPDX');
    $('.ui.dropdown.license').dropdown('set selected', selectedLicense);
  },

  /**
   * Resets the authors section
   * @method refreshAuthors
   */
  refreshAuthors() {
    this.taleAuthors.clear();
    this.addTaleAuthors();
  },

  /**
   * Adds the Tale's saved authors to the component property
   * that the handlebars template uses to create rows (this.taleAuthors)
   * @method addTaleAuthors
  */
 addTaleAuthors() {
  let authors = this.get('model').get("authors");
  this.taleAuthors.pushObjects(authors);
 },

  /**
   * Iterates over the array of saved&unsaved authors and makes sure that
   *   1. There is at least one author
   *   2. All of the authors have a first & last name and and an ORCID
   * @method validateAuthors
  */
  validateAuthors () {
    if (this.taleAuthors.length < 1) {
      this.set('errorMessage', 'Your Tale must have at least one author; please add one to the Tale.');
      return false
    }

    for (let i=0; i<this.taleAuthors.length; i++) {
      let author =  this.taleAuthors[i];
      if (author.lastName.length < 1) {
        this.set('errorMessage', "There is a Tale author that's missing a last name.");
        return false;
      }
      if (author.firstName.length < 1 ) {
        this.set('errorMessage', "There is a Tale author that's missing a first name.");
        return false;
      }
      if (author.orcid.length < 1) {
        this.set('errorMessage', "There is a Tale author that's missing an ORCID.");
        return false;
      }
    };
    return true;
  },

  actions: {

    /*
      Takes the fields in the UI and updates the Tale model with the new
      values.
    */
    updateTale() {
      const tale = this.get('model');
      const onFail = (e) => {
        this.set('errorMessage', ('Error saving tale (' + tale['id'] + '):', e));
        this.send('openErrorModal');
      };

      // Only update the Tale if the authors are valid
      if (this.validateAuthors()) {
        this.model.set('authors', this.taleAuthors);
        // Request that the Tale model be updated
        tale.save().catch(onFail);
      }
      else {
        this.send('openErrorModal');
      }
    },

    setTaleEnvironment: function(selected) {
      const tale = this.get('model');
      tale.set('imageId', selected);
    },

    setTaleLicense: function(selected) {
      const tale = this.get('model');
      tale.set('licenseSPDX', selected);
    },

     /**
     * Adds a new, blank user to the component authors object, which
     * then gets added as a new row in the UI.
     * @method addNewAuthor
    */
    addNewAuthor() {
      if (this.canEditTale) {
        let newAuthor = {
          'firstName': '',
          'lastName': '',
          'orcid':'',
        }
        this.taleAuthors.pushObject(newAuthor)
      }
    },

     /**
     * Removes an author from the component, and is consequentially removed
     * in the UI.
     * @method removeAuthor
    */
    removeAuthor(id) {
      if (this.canEditTale) {
        this.taleAuthors.removeAt(id);
      }
    },

     /**
     * Open the modal that the user sees when an error occurred or when validation failed.
     * @method openErrorModal
    */
    openErrorModal() {
      let selector = '.ui.metadata-error.modal';
      $(selector).modal('setting', 'closable', true);
      $(selector).modal('show');
    },

    /**
     * Generates an illustration for the tale.
     * @method generateIcon
    */
    generateIcon() {
      this.get('store').query('sils', {
        text: encodeURI(this.taleAuthors)
      })
        .then(sils => {
          sils.forEach(result => {
            let tale = this.get('model').get('tale');
            tale.set('illustration', result.get('icon'));
          })
        });
    },
  }
});
<commit_msg>Better error-handling for addAuthors<commit_after>import Component from '@ember/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { A } from '@ember/array';

import $ from 'jquery';

import config from '../../../config/environment';

const taleStatus = Object.create({
  NONE: -1,
  READ: 0,
  WRITE: 1,
  ADMIN: 2,
  SITE_ADMIN: 100
});

export default Component.extend({
  store: service(),
  apiHost: config.apiHost,
  environments: [],
  licenses: [],
  model: null,
  // Array of author dicts that the template uses to render rows
  taleAuthors: A(),
  // An error message that can get set, displayed in the error modal
  errorMessage: String(),
  model: null,
  // Flag that can be used to tell if the current user has permission to edit the Tale
  canEditTale: computed('model._accessLevel', function () {
    return this.get('model') && this.get('model') && this.get('model').get('_accessLevel') >= taleStatus.WRITE;
  }).readOnly(),
  cannotEditTale: computed.not('canEditTale').readOnly(),

  publishedURL: computed('model', 'model.publishInfo', function() {
    const tale = this.get('model');
    if (tale.publishInfo && tale.publishInfo.length) {
       return tale.publishInfo[tale.publishInfo.length - 1].uri;
    }
    return null;
  }),

  init() {
    this._super(...arguments);

    // Fetch images for user to select an environment
    const component = this;
    $.getJSON(this.get('apiHost') + '/api/v1/image/').then(function(images) {
      component.set('environments', images);
      component.selectDefaultImageId();
    });

    // Fetch licenses for users to select
    $.getJSON(this.get('apiHost') + '/api/v1/license/').then(function(licenses) {
      component.set('licenses', licenses);
      component.selectDefaultLicense();
    });

    this.refreshAuthors();
  },

  didRender() {
    // Enable dropdown functionality
    $('.ui.dropdown.image').dropdown();
    this.selectDefaultImageId();
    $('.ui.dropdown.license').dropdown();
    this.selectDefaultLicense();
  },

  didUpdateAttrs() {
    this._super(...arguments);
    this.refreshAuthors();
  },

  selectDefaultImageId() {
    // Select the current imageId by default
    const selectedImageId = this.get('model').get('imageId');
    $('.ui.dropdown.image').dropdown('set selected', selectedImageId);
  },

  selectDefaultLicense() {
    // Select the license that the Tale currently has
    const selectedLicense = this.get('model').get('licenseSPDX');
    $('.ui.dropdown.license').dropdown('set selected', selectedLicense);
  },

  /**
   * Resets the authors section
   * @method refreshAuthors
   */
  refreshAuthors() {
    this.taleAuthors.clear();
    this.addTaleAuthors();
  },

  /**
   * Adds the Tale's saved authors to the component property
   * that the handlebars template uses to create rows (this.taleAuthors)
   * @method addTaleAuthors
  */
  addTaleAuthors() {
    const model = this.model || { authors: [] }
    this.taleAuthors.pushObjects(model.authors);
  },

  /**
   * Iterates over the array of saved&unsaved authors and makes sure that
   *   1. There is at least one author
   *   2. All of the authors have a first & last name and and an ORCID
   * @method validateAuthors
  */
  validateAuthors () {
    if (this.taleAuthors.length < 1) {
      this.set('errorMessage', 'Your Tale must have at least one author; please add one to the Tale.');
      return false
    }

    for (let i=0; i<this.taleAuthors.length; i++) {
      let author =  this.taleAuthors[i];
      if (author.lastName.length < 1) {
        this.set('errorMessage', "There is a Tale author that's missing a last name.");
        return false;
      }
      if (author.firstName.length < 1 ) {
        this.set('errorMessage', "There is a Tale author that's missing a first name.");
        return false;
      }
      if (author.orcid.length < 1) {
        this.set('errorMessage', "There is a Tale author that's missing an ORCID.");
        return false;
      }
    };
    return true;
  },

  actions: {

    /*
      Takes the fields in the UI and updates the Tale model with the new
      values.
    */
    updateTale() {
      const tale = this.get('model');
      const onFail = (e) => {
        this.set('errorMessage', ('Error saving tale (' + tale['id'] + '):', e));
        this.send('openErrorModal');
      };

      // Only update the Tale if the authors are valid
      if (this.validateAuthors()) {
        this.model.set('authors', this.taleAuthors);
        // Request that the Tale model be updated
        tale.save().catch(onFail);
      }
      else {
        this.send('openErrorModal');
      }
    },

    setTaleEnvironment: function(selected) {
      const tale = this.get('model');
      tale.set('imageId', selected);
    },

    setTaleLicense: function(selected) {
      const tale = this.get('model');
      tale.set('licenseSPDX', selected);
    },

     /**
     * Adds a new, blank user to the component authors object, which
     * then gets added as a new row in the UI.
     * @method addNewAuthor
    */
    addNewAuthor() {
      if (this.canEditTale) {
        let newAuthor = {
          'firstName': '',
          'lastName': '',
          'orcid':'',
        }
        this.taleAuthors.pushObject(newAuthor)
      }
    },

     /**
     * Removes an author from the component, and is consequentially removed
     * in the UI.
     * @method removeAuthor
    */
    removeAuthor(id) {
      if (this.canEditTale) {
        this.taleAuthors.removeAt(id);
      }
    },

     /**
     * Open the modal that the user sees when an error occurred or when validation failed.
     * @method openErrorModal
    */
    openErrorModal() {
      let selector = '.ui.metadata-error.modal';
      $(selector).modal('setting', 'closable', true);
      $(selector).modal('show');
    },

    /**
     * Generates an illustration for the tale.
     * @method generateIcon
    */
    generateIcon() {
      this.get('store').query('sils', {
        text: encodeURI(this.taleAuthors)
      })
        .then(sils => {
          sils.forEach(result => {
            let tale = this.get('model').get('tale');
            tale.set('illustration', result.get('icon'));
          })
        });
    },
  }
});
<|endoftext|>