<commit_before>/**
 * Module to handle upload functionality.
 *
 * @mixin
 * @namespace Bolt.uploads
 *
 * @param {Object} bolt - The Bolt module.
 * @param {Object} $ - jQuery.
 */
(function (bolt, $) {
    'use strict';

    /**
     * Bolt.uploads mixin container.
     *
     * @private
     * @type {Object}
     */
    var uploads = {};

    /**
     * This function handles the setup of any field that requires upload capability.
     *
     * @static
     * @function bindField
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     */
    uploads.bindField = function (fieldset) {
        bindUpload(fieldset, false);
        bindSelectFromStack(fieldset);

        // Setup autocomplete popup.
        var accept = ($(fieldset).find('input[accept]').prop('accept') || '').replace(/\./g, ''),
            input = $(fieldset).find('input.path');

        input.autocomplete({
            source: bolt.conf('paths.async') + 'file/autocomplete?ext=' + encodeURIComponent(accept),
            minLength: 2,
            close: function () {
                $(input).trigger('change');
            }
        });
    };

    /**
     * This function handles the setup of any list fields that requires upload capability.
     *
     * @static
     * @function bindList
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     * @param {Object} messages
     */
    uploads.bindList = function (fieldset, messages) {
        var lastClick = null;

        $('div.list', fieldset)
            .sortable({
                helper: function (evt, item) {
                    if (!item.hasClass('selected')) {
                        item.toggleClass('selected');
                    }

                    return $('

');
                },
                start: function (evt, ui) {
                    var elements = $(fieldset).find('.selected').not('.ui-sortable-placeholder'),
                        len = elements.length,
                        currentOuterHeight = ui.placeholder.outerHeight(true),
                        currentInnerHeight = ui.placeholder.height(),
                        margin = parseInt(ui.placeholder.css('margin-top')) +
                            parseInt(ui.placeholder.css('margin-bottom'));

                    elements.hide();
                    ui.placeholder.height(currentInnerHeight + len * currentOuterHeight - currentOuterHeight - margin);
                    ui.item.data('items', elements);
                },
                beforeStop: function (evt, ui) {
                    ui.item.before(ui.item.data('items'));
                },
                stop: function () {
                    $(fieldset).find('.selected').show();
                    serializeList(fieldset);
                },
                delay: 100,
                distance: 5
            })
            .on('click', '.list-item', function (evt) {
                if ($(evt.target).hasClass('list-item')) {
                    if (evt.shiftKey) {
                        if (lastClick) {
                            var currentIndex = $(this).index(),
                                lastIndex = lastClick.index();

                            if (lastIndex > currentIndex) {
                                $(this).nextUntil(lastClick).add(this).add(lastClick).addClass('selected');
                            } else if (lastIndex < currentIndex) {
                                $(this).prevUntil(lastClick).add(this).add(lastClick).addClass('selected');
                            } else {
                                $(this).toggleClass('selected');
                            }
                        }
                    } else if (evt.ctrlKey || evt.metaKey) {
                        $(this).toggleClass('selected');
                    } else {
                        $(fieldset).find('.list-item').not($(this)).removeClass('selected');
                        $(this).toggleClass('selected');
                    }

                    lastClick = evt.shiftKey || evt.ctrlKey || evt.metaKey || $(this).hasClass('selected') ?
                        $(this) : null;
                }
            })
            .on('click', '.remove-button', function (evt) {
                evt.preventDefault();

                if (confirm(messages.removeSingle)) {
                    $(this).closest('.list-item').remove();
                    serializeList(fieldset);
                }
            })
            .on('change', 'input', function () {
                serializeList(fieldset);
            });

        $(fieldset).find('.remove-selected-button').on('click', function () {
            if (confirm(messages.removeMulti)) {
                $(fieldset).find('.selected').closest('.list-item').remove();
                serializeList(fieldset);
            }
        });

        bindUpload(fieldset, true);
        bindSelectFromStack(fieldset);
    };

    /**
     * Bind upload capability to the stack.
     *
     * @static
     * @function bindStack
     * @memberof Bolt.uploads
     * @param {Object} container
     */
    uploads.bindStack = function (container) {
        bindUpload(container);
    };

    /**
     * Adds a file to an upload list.
     *
     * @static
     * @function addToList
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     * @param {string} filename
     * @param {string=} title (Optional)
     */
    uploads.addToList = function (fieldset, filename, title) {
        var listField = $('div.list', fieldset),
            type = $(fieldset).data('bolt-fieldset'),
            templateItem = type === 'filelist' ? 'field.filelist.template.item' : 'field.imagelist.template.item';

        // Remove empty list message, if there.
        $('>p', listField).remove();

        // Append to list.
        listField.append(
            $(Bolt.data(
                templateItem,
                {
                    '%TITLE_A%':    title || filename,
                    '%FILENAME_E%': $('
').text(filename).html(), // Escaped
                    '%FILENAME_A%': filename
                }
            ))
        );

        serializeList(fieldset);
    };

    /**
     * This function works at a lower level than the bindField function, it sets up the handlers for the upload
     * button along with drag and drop functionality. To do this it uses the `key` parameter which needs to
     * be a unique ID.
     *
     * @private
     * @function bindUpload
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     */
    function bindUpload(fieldset) {
        var fileInput = $(fieldset).find('input[type=file]'),
            dropZone = $(fieldset).find('.dropzone'),
            //
            maxSize = bolt.conf('uploadConfig.maxSize'),
            accept = $(fileInput).attr('accept'),
            extensions = accept ? accept.replace(/^\./, '').split(/,\./) : [],
            pattern = new RegExp('(\\.|\\/)(' + extensions.join('|') + ')$', 'i');

        fileInput
            .fileupload({
                dataType: 'json',
                dropZone: dropZone,
                pasteZone: null,
                maxFileSize: maxSize > 0 ? maxSize : undefined,
                minFileSize: undefined,
                acceptFileTypes: accept ? pattern : undefined,
                maxNumberOfFiles: undefined,
                messages: {
                    maxFileSize: '>:' + humanBytes(maxSize),
                    minFileSize: '<',
                    acceptFileTypes: 'T:.' + extensions.join(', .'),
                    maxNumberOfFiles: '#'
                }
            })
            .on('fileuploadprocessfail', onProcessFail)
            .on('fileuploadsubmit', onUploadSubmit)
            .on('fileuploadprogress', onUploadProgress)
            .on('fileuploadalways', onUploadAlways)
            .on('fileuploaddone', onUploadDone);
    }

    /**
     * Binds event to select from stack button.
     *
     * @private
     * @function bindUpload
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     */
    function bindSelectFromStack(fieldset) {
        $('ul.select-from-stack a', fieldset).on('click', function () {
            var path = $(this).data('path');

            // Close the dropdown.
            $(this).closest('.btn-group').find('button.dropdown-toggle').dropdown('toggle');

            if (fieldset.is(':bolt-fieldFile') || fieldset.is(':bolt-fieldImage')) {
                $('input.path', fieldset).val(path).trigger('change');
            } else if (fieldset.is(':bolt-fieldFilelist') || fieldset.is(':bolt-fieldImagelist')) {
                uploads.addToList(fieldset, path);
            } else if (fieldset.is(':bolt-buicStack')) {
                fieldset.buicStack('add', path);
            }

            return false;
        });
    }

    /**
     * Upload processing failed.
     *
     * @private
     * @function onProcessFail
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onProcessFail(event, data) {
        var currentFile = data.files[data.index],
                type = currentFile.error.substr(0, 1),
                alert,
                context = {
                    '%FILENAME%': currentFile.name,
                    '%FILESIZE%': humanBytes(currentFile.size),
                    '%FILETYPE%': currentFile.type,
                    '%ALLOWED%': currentFile.error.substr(2)
                };

        switch (type) {
            case '>':
                alert = bolt.data('field.uploads.template.large-file', context);
                break;

            case 'T':
                alert = bolt.data('field.uploads.template.wrong-type', context);
                break;

            default:
                alert = '
' + currentFile.error + '
';
        }
        bootbox.alert(alert);
    }

    /**
     * Upload starts.
     *
     * @private
     * @function onUploadSubmit
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadSubmit(event, data) {
        var progress = $(event.target).closest('fieldset').find(':bolt-buicProgress');

        $.each(data.files, function () {
            progress.buicProgress('add', this.name);
        });
    }

    /**
     * Signal upload progress.
     *
     * @private
     * @function onUploadProgress
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadProgress(event, data) {
        var progress = $(event.target).closest('fieldset').find(':bolt-buicProgress');

        $.each(data.files, function () {
            progress.buicProgress('set', this.name, data.loaded / data.total);
        });
    }

    /**
     * After successful or failed upload.
     *
     * @private
     * @function onUploadAlways
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadAlways(event, data) {
        var progress = $(event.target).closest('fieldset').find(':bolt-buicProgress');

        $.each(data.files, function () {
            progress.buicProgress('remove', this.name);
        });
    }

    /**
     * Files successfully uploaded.
     *
     * @private
     * @function onUploadDone
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadDone(event, data) {
        var fieldset = $(event.target).closest('fieldset');

        $.each(data.result, function (idx, file) {
            if (file.error) {
                bootbox.alert(bolt.data('field.uploads.template.error', {'%ERROR%': file.error}));
            } else {
                if (fieldset.is(':bolt-fieldFile') || fieldset.is(':bolt-fieldImage')) {
                    $('input.path', fieldset).val(file.name).trigger('change');
                    bolt.stack.addToStack(file.name);
                } else if (fieldset.is(':bolt-fieldFilelist') || fieldset.is(':bolt-fieldImagelist')) {
                    uploads.addToList(fieldset, file.name);
                } else if (fieldset.is(':bolt-buicStack')) {
                    fieldset.buicStack('add', file.name);
                }
            }
        });
    }

    /**
     * Human readable formatted bytes.
     *
     * @private
     * @function humanBytes
     * @memberof Bolt.uploads
     *
     * @param {integer} val - Value to format.
     */
    function humanBytes(val) {
        var units = ' kMGTPEZY',
            u = -1;

        while (++u < 8 && Math.abs(val) >= 1000) {
            val /= 1000;
        }

        if (!!(typeof Intl === 'object' && Intl && typeof Intl.NumberFormat === 'function')) {
            val = val.toLocaleString(
                bolt.conf('locale.long').replace(/_/g, '-'),
                {maximumSignificantDigits: 3}
            );
        } else {
            val = val.toFixed(2);
        }

        return val + ' ' + units[u].trim() + 'B';
    }

    /**
     * Serialize list data on change.
     *
     * @private
     * @function serializeList
     * @memberof Bolt.uploads
     *
     * @param {Object} fieldset
     */
    function serializeList(fieldset) {
        var listField = $('div.list', fieldset),
            dataField = $('textarea', fieldset),
            isFile = $(fieldset).data('bolt-fieldset') === 'filelist',
            templateEmpty = isFile ? 'field.filelist.template.empty' : 'field.imagelist.template.empty',
            data = [];

        $('.list-item', listField).each(function () {
            data.push({
                filename: $(this).find('input.filename').val(),
                title: $(this).find('input.title').val()
            });
        });
        dataField.val(JSON.stringify(data));

        // Display empty list message.
        if (data.length === 0) {
            listField.html(Bolt.data(templateEmpty));
        }
    }

    // Apply mixin container
    bolt.uploads = uploads;

})(Bolt || {}, jQuery);
<commit_msg>Simplify upload path adding<commit_after>/**
 * Module to handle upload functionality.
 *
 * @mixin
 * @namespace Bolt.uploads
 *
 * @param {Object} bolt - The Bolt module.
 * @param {Object} $ - jQuery.
 */
(function (bolt, $) {
    'use strict';

    /**
     * Bolt.uploads mixin container.
     *
     * @private
     * @type {Object}
     */
    var uploads = {};

    /**
     * This function handles the setup of any field that requires upload capability.
     *
     * @static
     * @function bindField
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     */
    uploads.bindField = function (fieldset) {
        bindUpload(fieldset, false);
        bindSelectFromStack(fieldset);

        // Setup autocomplete popup.
        var accept = ($(fieldset).find('input[accept]').prop('accept') || '').replace(/\./g, ''),
            input = $(fieldset).find('input.path');

        input.autocomplete({
            source: bolt.conf('paths.async') + 'file/autocomplete?ext=' + encodeURIComponent(accept),
            minLength: 2,
            close: function () {
                $(input).trigger('change');
            }
        });
    };

    /**
     * This function handles the setup of any list fields that requires upload capability.
     *
     * @static
     * @function bindList
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     * @param {Object} messages
     */
    uploads.bindList = function (fieldset, messages) {
        var lastClick = null;

        $('div.list', fieldset)
            .sortable({
                helper: function (evt, item) {
                    if (!item.hasClass('selected')) {
                        item.toggleClass('selected');
                    }

                    return $('

');
                },
                start: function (evt, ui) {
                    var elements = $(fieldset).find('.selected').not('.ui-sortable-placeholder'),
                        len = elements.length,
                        currentOuterHeight = ui.placeholder.outerHeight(true),
                        currentInnerHeight = ui.placeholder.height(),
                        margin = parseInt(ui.placeholder.css('margin-top')) +
                            parseInt(ui.placeholder.css('margin-bottom'));

                    elements.hide();
                    ui.placeholder.height(currentInnerHeight + len * currentOuterHeight - currentOuterHeight - margin);
                    ui.item.data('items', elements);
                },
                beforeStop: function (evt, ui) {
                    ui.item.before(ui.item.data('items'));
                },
                stop: function () {
                    $(fieldset).find('.selected').show();
                    serializeList(fieldset);
                },
                delay: 100,
                distance: 5
            })
            .on('click', '.list-item', function (evt) {
                if ($(evt.target).hasClass('list-item')) {
                    if (evt.shiftKey) {
                        if (lastClick) {
                            var currentIndex = $(this).index(),
                                lastIndex = lastClick.index();

                            if (lastIndex > currentIndex) {
                                $(this).nextUntil(lastClick).add(this).add(lastClick).addClass('selected');
                            } else if (lastIndex < currentIndex) {
                                $(this).prevUntil(lastClick).add(this).add(lastClick).addClass('selected');
                            } else {
                                $(this).toggleClass('selected');
                            }
                        }
                    } else if (evt.ctrlKey || evt.metaKey) {
                        $(this).toggleClass('selected');
                    } else {
                        $(fieldset).find('.list-item').not($(this)).removeClass('selected');
                        $(this).toggleClass('selected');
                    }

                    lastClick = evt.shiftKey || evt.ctrlKey || evt.metaKey || $(this).hasClass('selected') ?
                        $(this) : null;
                }
            })
            .on('click', '.remove-button', function (evt) {
                evt.preventDefault();

                if (confirm(messages.removeSingle)) {
                    $(this).closest('.list-item').remove();
                    serializeList(fieldset);
                }
            })
            .on('change', 'input', function () {
                serializeList(fieldset);
            });

        $(fieldset).find('.remove-selected-button').on('click', function () {
            if (confirm(messages.removeMulti)) {
                $(fieldset).find('.selected').closest('.list-item').remove();
                serializeList(fieldset);
            }
        });

        bindUpload(fieldset, true);
        bindSelectFromStack(fieldset);
    };

    /**
     * Bind upload capability to the stack.
     *
     * @static
     * @function bindStack
     * @memberof Bolt.uploads
     * @param {Object} container
     */
    uploads.bindStack = function (container) {
        bindUpload(container);
    };

    /**
     * Adds a file to an upload list.
     *
     * @static
     * @function addToList
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     * @param {string} filename
     * @param {string=} title (Optional)
     */
    uploads.addToList = function (fieldset, filename, title) {
        var listField = $('div.list', fieldset),
            type = $(fieldset).data('bolt-fieldset'),
            templateItem = type === 'filelist' ? 'field.filelist.template.item' : 'field.imagelist.template.item';

        // Remove empty list message, if there.
        $('>p', listField).remove();

        // Append to list.
        listField.append(
            $(Bolt.data(
                templateItem,
                {
                    '%TITLE_A%':    title || filename,
                    '%FILENAME_E%': $('
').text(filename).html(), // Escaped
                    '%FILENAME_A%': filename
                }
            ))
        );

        serializeList(fieldset);
    };

    /**
     * This function works at a lower level than the bindField function, it sets up the handlers for the upload
     * button along with drag and drop functionality. To do this it uses the `key` parameter which needs to
     * be a unique ID.
     *
     * @private
     * @function bindUpload
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     */
    function bindUpload(fieldset) {
        var fileInput = $(fieldset).find('input[type=file]'),
            dropZone = $(fieldset).find('.dropzone'),
            //
            maxSize = bolt.conf('uploadConfig.maxSize'),
            accept = $(fileInput).attr('accept'),
            extensions = accept ? accept.replace(/^\./, '').split(/,\./) : [],
            pattern = new RegExp('(\\.|\\/)(' + extensions.join('|') + ')$', 'i');

        fileInput
            .fileupload({
                dataType: 'json',
                dropZone: dropZone,
                pasteZone: null,
                maxFileSize: maxSize > 0 ? maxSize : undefined,
                minFileSize: undefined,
                acceptFileTypes: accept ? pattern : undefined,
                maxNumberOfFiles: undefined,
                messages: {
                    maxFileSize: '>:' + humanBytes(maxSize),
                    minFileSize: '<',
                    acceptFileTypes: 'T:.' + extensions.join(', .'),
                    maxNumberOfFiles: '#'
                }
            })
            .on('fileuploadprocessfail', onProcessFail)
            .on('fileuploadsubmit', onUploadSubmit)
            .on('fileuploadprogress', onUploadProgress)
            .on('fileuploadalways', onUploadAlways)
            .on('fileuploaddone', onUploadDone);
    }

    /**
     * Binds event to select from stack button.
     *
     * @private
     * @function bindUpload
     * @memberof Bolt.uploads
     * @param {Object} fieldset
     */
    function bindSelectFromStack(fieldset) {
        $('ul.select-from-stack a', fieldset).on('click', function () {
            var path = $(this).data('path');

            // Close the dropdown.
            $(this).closest('.btn-group').find('button.dropdown-toggle').dropdown('toggle');

            add(fieldset, path, false);

            return false;
        });
    }

    /**
     * Upload processing failed.
     *
     * @private
     * @function onProcessFail
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onProcessFail(event, data) {
        var currentFile = data.files[data.index],
                type = currentFile.error.substr(0, 1),
                alert,
                context = {
                    '%FILENAME%': currentFile.name,
                    '%FILESIZE%': humanBytes(currentFile.size),
                    '%FILETYPE%': currentFile.type,
                    '%ALLOWED%': currentFile.error.substr(2)
                };

        switch (type) {
            case '>':
                alert = bolt.data('field.uploads.template.large-file', context);
                break;

            case 'T':
                alert = bolt.data('field.uploads.template.wrong-type', context);
                break;

            default:
                alert = '
' + currentFile.error + '
';
        }
        bootbox.alert(alert);
    }

    /**
     * Upload starts.
     *
     * @private
     * @function onUploadSubmit
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadSubmit(event, data) {
        var progress = $(event.target).closest('fieldset').find(':bolt-buicProgress');

        $.each(data.files, function () {
            progress.buicProgress('add', this.name);
        });
    }

    /**
     * Signal upload progress.
     *
     * @private
     * @function onUploadProgress
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadProgress(event, data) {
        var progress = $(event.target).closest('fieldset').find(':bolt-buicProgress');

        $.each(data.files, function () {
            progress.buicProgress('set', this.name, data.loaded / data.total);
        });
    }

    /**
     * After successful or failed upload.
     *
     * @private
     * @function onUploadAlways
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadAlways(event, data) {
        var progress = $(event.target).closest('fieldset').find(':bolt-buicProgress');

        $.each(data.files, function () {
            progress.buicProgress('remove', this.name);
        });
    }

    /**
     * Files successfully uploaded.
     *
     * @private
     * @function onUploadDone
     * @memberof Bolt.uploads
     * @param {Object} event
     * @param {Object} data
     */
    function onUploadDone(event, data) {
        var fieldset = $(event.target).closest('fieldset');

        $.each(data.result, function (idx, file) {
            if (file.error) {
                bootbox.alert(bolt.data('field.uploads.template.error', {'%ERROR%': file.error}));
            } else {
                add(fieldset, file.name, true);
            }
        });
    }

    /**
     * Human readable formatted bytes.
     *
     * @private
     * @function humanBytes
     * @memberof Bolt.uploads
     *
     * @param {integer} val - Value to format.
     */
    function humanBytes(val) {
        var units = ' kMGTPEZY',
            u = -1;

        while (++u < 8 && Math.abs(val) >= 1000) {
            val /= 1000;
        }

        if (!!(typeof Intl === 'object' && Intl && typeof Intl.NumberFormat === 'function')) {
            val = val.toLocaleString(
                bolt.conf('locale.long').replace(/_/g, '-'),
                {maximumSignificantDigits: 3}
            );
        } else {
            val = val.toFixed(2);
        }

        return val + ' ' + units[u].trim() + 'B';
    }

    /**
     * Serialize list data on change.
     *
     * @private
     * @function serializeList
     * @memberof Bolt.uploads
     *
     * @param {Object} fieldset
     */
    function serializeList(fieldset) {
        var listField = $('div.list', fieldset),
            dataField = $('textarea', fieldset),
            isFile = $(fieldset).data('bolt-fieldset') === 'filelist',
            templateEmpty = isFile ? 'field.filelist.template.empty' : 'field.imagelist.template.empty',
            data = [];

        $('.list-item', listField).each(function () {
            data.push({
                filename: $(this).find('input.filename').val(),
                title: $(this).find('input.title').val()
            });
        });
        dataField.val(JSON.stringify(data));

        // Display empty list message.
        if (data.length === 0) {
            listField.html(Bolt.data(templateEmpty));
        }
    }

    /**
     * Add the file.
     *
     * @private
     * @function add
     * @memberof Bolt.uploads
     *
     * @param {Object} fieldset
     * @param {string} path
     * @param {boolean} stackAdd
     */
    function add(fieldset, path, stackAdd) {
        if (fieldset.is(':bolt-fieldFile') || fieldset.is(':bolt-fieldImage')) {
            $('input.path', fieldset).val(path).trigger('change');
            if (stackAdd) {
                bolt.stack.addToStack(path);
            }
        } else if (fieldset.is(':bolt-fieldFilelist') || fieldset.is(':bolt-fieldImagelist')) {
            uploads.addToList(fieldset, path);
        } else if (fieldset.is(':bolt-buicStack')) {
            fieldset.buicStack('add', path);
        }
    }

    // Apply mixin container
    bolt.uploads = uploads;

})(Bolt || {}, jQuery);
<|endoftext|>


<commit_before>/**
 * jaylist is a wrapper for javascript's built-in hash storage system.
 * It provides functions to manipulate the list.
 *
 * Copyright (C) 2011 by Ben Brooks Scholz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * List allows access to elements through a key (string of characters).
 */
var List = function () {
    this.table = {};
};

/**
 * List functions to create and manipulate a hashed list.
 *
 *  get(key)          -- Returns the value associated with key, un-
 *                       defined if there is no values associated
 *                       with the key.
 *  add(key, value)   -- Insert an object into the list, overwriting
 *                       any value already assigned to the key. Returns
 *                       the value upon successful addition.
 *  remove(key)       -- Removes the object from the list. Returns
 *                       undefined if no value is assigned to the key.
 *                       Upon successful removal, it returns the value
 *                       removed.
 *  keys()            -- Returns an array of the keys in the list.
 *  values()          -- Returns an array of the values in the list
 *                       with each value appearing only once.
 *  items()           -- Returns an array of key value pairs.
 *  len()             -- Returns the number of elements in the list,
 *                       zero if empty.
 *  clear()           -- Removes the items from the list.
 *  hasKey(key)       -- Returns true if the list contains the key and
 *                       false if it does not.
 *  pop(key, default) -- Returns the value associated with the key and
 *                       removes it. If the value doesn't exist return
 *                       default. Default is an optional argument that
 *                       is set to undefined.
 *  popItem()         -- Returns a random key value pair ( [key, value] )
 *                       and removes it. Like it's python counterpart,
 *                       you can use it to destructively iterate over
 *                       the list.
 *  update(list)      -- Updates the list with the entries of the input
 *                       list.
 *  copy()            -- Returns a deep copy of the list.
 *  each(callback)    -- Iterates over the entries in the list, passing
 *                       the parameter 'key'.
 *
 */
List.prototype = {

    /**
     * get: Returns the value attached to the given key or undefined
     * if it isn't found.
     */
    get: function(key) {
        return this.table[key];
    },

    /**
     * add: Inserts an object into the list, assigning it to the given key.
     * It returns the value upon successful addition to the list. If a value
     * is inserted with a key that exists in the list already, the old value
     * is overwritten.
     */
    add: function (key, value) {
        return (this.table[key] = value);
    },

    /**
     * remove: Removes an object from the list with the given key. It returns
     * undefined if no object with the given key exists in the list. Otherwise,
     * it returns the value removed.
     */
    remove: function (key) {
        var val = this.table[key];

        delete this.table[key];

        return val;
    },

    /**
     * keys: Returns an array of the keys in the List.
     */
    keys: function () {
        var key, keys = [];

        // iterate through the hash pairs in the table, pushing each key
        // to the keys array.
        for (key in this.table)
            if (this.table.hasOwnProperty(key))
                keys.push(key);

        return keys;
    },

    /**
     * values: Returns an array of the values in the list. Duplicate values
     * are only displayed once.
     */
    values: function () {
        var i, key, contains, values = [];

        // iterate through the keys in the list
        for (key in this.table) {
            if (this.table.hasOwnProperty(key)) {

                // decrement the value for the length of values.
                // contains is false if the value does not already
                // appear in the values array and true if it is
                // found. when contains is true, break out of the loop.
                contains = false;
                i = values.length;
                while (i-- && !contains)
                    if (this.get(key) === values[i])
                        contains = true;

                // if the value is not already in the values array,
                // push the value
                if (!contains)
                    values.push(this.get(key));
            }
        }

        return values;
    },

    /**
     * items: Returns an array of key-value pairs: [[key, value]]
     */
    items: function () {
        var key, itemlist = [];

        for (key in this.table)
            if (this.table.hasOwnProperty(key))
                itemlist.push([key, this.table[key]]);

        return itemlist;
    },

    /**
     * len: Returns the number of items in the list. Returns zero if empty.
     */
    len: function () {
        var entry, len = 0;

        // count the entries in the table
        for (entry in this.table)
            if (this.table.hasOwnProperty(entry))
                len = len + 1;

        return len;
    },

    /**
     * clear: Removes the items from the list.
     */
    clear: function () {
        for (var key in this.table)
            if (this.table.hasOwnProperty(key))
                delete this.table[key];
    },

    /**
     * hasKey: Returns true if the list contains the given key and false if
     * if does not.
     */
    hasKey: function(key) {
        return (this.get(key) !== undefined);
    },

    /**
     * pop: Returns the value associated with the key and removes it. If there
     * is no value associated with the key, return undefined, & for that reason
     * def is optional.
     */
    pop: function (key, def) {
        return this.hasKey(key) ? this.remove(key) : def;
    },

    /**
     * popItem: Returns a random key value pair ( [key, value] ) and removes it.
     */
    popItem: function () {
        var pair, keys = this.keys();

        // pop a random key from the key list and push it to the pair
        pair = [keys[Math.floor(Math.random() * keys.length)]];

        // push the value to the pair and remove it
        pair.push(this.remove(pair[0]));

        return pair;
    },

    /**
     * update: Adds all the entries from the input list to the list.
     */
    update: function (list) {
        var key;

        // iterate through the input list, push the entries to the list
        for (key in list.table)
            if (list.table.hasOwnProperty(key))
                this.table[key] = list.table[key];
    },

    /**
     * copy: Returns a deep copy of the list.
     */
    copy: function () {
        // recursive function for deep copying a list
        var deepCopy = function (obj) {
            var entry, copied = {};

            if (typeof(obj) === 'object') {
                if (typeof(obj.length) === 'number')
                    copied = [];
                for (entry in obj) {
                    if (obj.hasOwnProperty(entry)) {
                        if (typeof(obj[entry]) === 'object')
                            copied[entry] = deepCopy(obj[entry]);
                        else if (typeof(obj[entry]) === 'string' || 'number')
                            copied[entry] = obj[entry];
                        else if (typeof(obj[entry]) === 'boolean')
                            copied[entry] = (typeof(obj[entry])) ? true : false;
                    }
                }
            }
            return copied;
        };
        var copy = new List();
        copy.update(deepCopy(this));

        return copy;
    },

    /**
     * each: Iterates over each entry in the list, calling the callback with the
     * parameter 'key'.
     */
    each: function (callback) {
        for (var entry in this.table)
            if (this.table.hasOwnProperty(entry))
                callback(entry);
    }
};<commit_msg>copy() now also copies functions of an object<commit_after>/**
 * jaylist is a wrapper for javascript's built-in hash storage system.
 * It provides functions to manipulate the list.
 *
 * Copyright (C) 2011 by Ben Brooks Scholz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * List allows access to elements through a key (string of characters).
 */
var List = function () {
    this.table = {};
};

/**
 * List functions to create and manipulate a hashed list.
 *
 *  get(key)          -- Returns the value associated with key, un-
 *                       defined if there is no values associated
 *                       with the key.
 *  add(key, value)   -- Insert an object into the list, overwriting
 *                       any value already assigned to the key. Returns
 *                       the value upon successful addition.
 *  remove(key)       -- Removes the object from the list. Returns
 *                       undefined if no value is assigned to the key.
 *                       Upon successful removal, it returns the value
 *                       removed.
 *  keys()            -- Returns an array of the keys in the list.
 *  values()          -- Returns an array of the values in the list
 *                       with each value appearing only once.
 *  items()           -- Returns an array of key value pairs.
 *  len()             -- Returns the number of elements in the list,
 *                       zero if empty.
 *  clear()           -- Removes the items from the list.
 *  hasKey(key)       -- Returns true if the list contains the key and
 *                       false if it does not.
 *  pop(key, default) -- Returns the value associated with the key and
 *                       removes it. If the value doesn't exist return
 *                       default. Default is an optional argument that
 *                       is set to undefined.
 *  popItem()         -- Returns a random key value pair ( [key, value] )
 *                       and removes it. Like it's python counterpart,
 *                       you can use it to destructively iterate over
 *                       the list.
 *  update(list)      -- Updates the list with the entries of the input
 *                       list.
 *  copy()            -- Returns a deep copy of the list.
 *  each(callback)    -- Iterates over the entries in the list, passing
 *                       the parameter 'key'.
 *
 */
List.prototype = {

    /**
     * get: Returns the value attached to the given key or undefined
     * if it isn't found.
     */
    get: function(key) {
        return this.table[key];
    },

    /**
     * add: Inserts an object into the list, assigning it to the given key.
     * It returns the value upon successful addition to the list. If a value
     * is inserted with a key that exists in the list already, the old value
     * is overwritten.
     */
    add: function (key, value) {
        return (this.table[key] = value);
    },

    /**
     * remove: Removes an object from the list with the given key. It returns
     * undefined if no object with the given key exists in the list. Otherwise,
     * it returns the value removed.
     */
    remove: function (key) {
        var val = this.table[key];

        delete this.table[key];

        return val;
    },

    /**
     * keys: Returns an array of the keys in the List.
     */
    keys: function () {
        var key, keys = [];

        // iterate through the hash pairs in the table, pushing each key
        // to the keys array.
        for (key in this.table)
            if (this.table.hasOwnProperty(key))
                keys.push(key);

        return keys;
    },

    /**
     * values: Returns an array of the values in the list. Duplicate values
     * are only displayed once.
     */
    values: function () {
        var i, key, contains, values = [];

        // iterate through the keys in the list
        for (key in this.table) {
            if (this.table.hasOwnProperty(key)) {

                // decrement the value for the length of values.
                // contains is false if the value does not already
                // appear in the values array and true if it is
                // found. when contains is true, break out of the loop.
                contains = false;
                i = values.length;
                while (i-- && !contains)
                    if (this.get(key) === values[i])
                        contains = true;

                // if the value is not already in the values array,
                // push the value
                if (!contains)
                    values.push(this.get(key));
            }
        }

        return values;
    },

    /**
     * items: Returns an array of key-value pairs: [[key, value]]
     */
    items: function () {
        var key, itemlist = [];

        for (key in this.table)
            if (this.table.hasOwnProperty(key))
                itemlist.push([key, this.table[key]]);

        return itemlist;
    },

    /**
     * len: Returns the number of items in the list. Returns zero if empty.
     */
    len: function () {
        var entry, len = 0;

        // count the entries in the table
        for (entry in this.table)
            if (this.table.hasOwnProperty(entry))
                len = len + 1;

        return len;
    },

    /**
     * clear: Removes the items from the list.
     */
    clear: function () {
        for (var key in this.table)
            if (this.table.hasOwnProperty(key))
                delete this.table[key];
    },

    /**
     * hasKey: Returns true if the list contains the given key and false if
     * if does not.
     */
    hasKey: function(key) {
        return (this.get(key) !== undefined);
    },

    /**
     * pop: Returns the value associated with the key and removes it. If there
     * is no value associated with the key, return undefined, & for that reason
     * def is optional.
     */
    pop: function (key, def) {
        return this.hasKey(key) ? this.remove(key) : def;
    },

    /**
     * popItem: Returns a random key value pair ( [key, value] ) and removes it.
     */
    popItem: function () {
        var pair, keys = this.keys();

        // pop a random key from the key list and push it to the pair
        pair = [keys[Math.floor(Math.random() * keys.length)]];

        // push the value to the pair and remove it
        pair.push(this.remove(pair[0]));

        return pair;
    },

    /**
     * update: Adds all the entries from the input list to the list.
     */
    update: function (list) {
        var key;

        // iterate through the input list, push the entries to the list
        for (key in list.table)
            if (list.table.hasOwnProperty(key))
                this.table[key] = list.table[key];
    },

    /**
     * copy: Returns a deep copy of the list.
     */
    copy: function () {
        // recursive function for deep copying a list
        var deepCopy = function (obj) {
            var entry, copied = {};

            if (typeof(obj) === 'object') {
                if (typeof(obj.length) === 'number')
                    copied = [];
                for (entry in obj) {
                    if (obj.hasOwnProperty(entry)) {
                        if (typeof(obj[entry]) === 'object')
                            copied[entry] = deepCopy(obj[entry]);
                        else if (typeof(obj[entry]) === 'string' || 'number' || 'function')
                            copied[entry] = obj[entry];
                        else if (typeof(obj[entry]) === 'boolean')
                            copied[entry] = (typeof(obj[entry])) ? true : false;
                    }
                }
            }
            return copied;
        };
        var copy = new List();
        copy.update(deepCopy(this));

        return copy;
    },

    /**
     * each: Iterates over each entry in the list, calling the callback with the
     * parameter 'key'.
     */
    each: function (callback) {
        for (var key in this.table)
            if (this.table.hasOwnProperty(key))
                callback(key);
    }
};<|endoftext|>


<commit_before>/* nor-nopg */

var debug = require('nor-debug');
var Q = require('q');
var pg = require('nor-pg');
var extend = require('nor-extend').setup({useFunctionPromises:true});
var NoPgObject = require('./Object.js');
var NoPgType = require('./Type.js');
var NoPgAttachment = require('./Attachment.js');
var NoPgLib = require('./Lib.js');

/** */
function assert(valid, text) {
	if(!valid) {
		throw new TypeError(text);
	}
}

/** Assert that the `doc` is NoPgObject */
function assert_type(doc, type, text) {
	assert(doc instanceof type, text || "Not correct type: " + type);
}

/** The constructor */
function NoPG(db) {
	var self = this;
	self._db = db;
	self._values = [];
}

module.exports = NoPG;

// Object constructors
NoPG.Object = NoPgObject;

/** Start */
NoPG.start = function(pgconfig) {
	return extend.promise( [NoPG], pg.start(pgconfig).then(function(db) {
		return new NoPG(db);
	}));
};

/** Fetch next value from queue */
NoPG.prototype.fetch = function() {
	return this._values.shift();
};

/** Get object handler */
function get_object(rows) {
	var doc = rows.shift();
	if(!doc) { throw new TypeError("failed to parse object"); }
	var obj = {};
	Object.keys(doc).forEach(function(key) {
		obj['$'+key] = doc[key];
	});
	return new NoPgObject(obj);
}

/** Save object handler */
function save_object_to(self) {
	if(self instanceof NoPgObject) {
		return function(doc) {
			return self.update(doc);
		};
	}

	if(self._values) {
		return function(doc) {
			self._values.push( doc );
			return self;
		};
	}

	throw new TypeError("Unknown target");
}

/** Perform query */
function do_query(self, query, values) {
	return extend.promise( [NoPG], self._db._query(query, values) );
}

/** Commit transaction */
NoPG.prototype.commit = function() {
	return extend.promise( [NoPG], this._db.commit() );
};

/** Initialize the database */
NoPG.prototype.init = function() {
	var self = this;
	var builders = require('./schema/');
	return builders.reduce(function (so_far, f) {
	    return so_far.then(function(db) {
			db.fetchAll();
			return db;
		}).then(f);
	}, Q(self._db)).then(function() { return self; });
};

/** Create object by type: `db.create([TYPE])([OPT(S)])`. */
NoPG.prototype.create = function(type) {
	debug.log('at create(', type, ')');
	var self = this;
	function create2(data) {
		debug.log('at create2(', data, ')');

		var query, params;

		if(type !== undefined) {
			if(typeof type === 'string') {
				query = "INSERT INTO objects (content, types_id) VALUES ($1, get_type($2)) RETURNING *";
				params = [data, type];
			} else if(type instanceof NoPgType) {
				query = "INSERT INTO objects (content, types_id) VALUES ($1, $2) RETURNING *";
				params = [data, type.id];
			} else {
				throw new TypeError("unknown type: " + type);
			}
		} else {
			query = "INSERT INTO objects (content) VALUES ($1) RETURNING *";
			params = [data];
		}

		return do_query(self, query, params).then(get_object).then(save_object_to(self));
	}
	return create2;
};

/** Update object */
NoPG.prototype.update = function(doc, data) {
	var self = this;
	assert_type(doc, NoPgObject, "doc is not NoPg.Object");
	return do_query(self, "UPDATE objects SET content = $1 RETURNING *", [data]).then(get_object).then(save_object_to(doc)).then(function() { return self; });
};

/** Create type object by type: `db.createType([TYPE-NAME])([OPT(S)])`. */
NoPG.prototype.createType = function(name) {
	debug.log('at createType(', name, ')');
	var self = this;
	function create2(opts) {
		opts = opts || {};
		debug.log('at createType2(', opts, ')');
		var schema = opts.schema || {};
		var validator = (''+opts.validator) || null;
		var meta = opts.meta || {};
		return do_query(self, "INSERT INTO types (name, schema, validator, meta) VALUES ($1, $2, $3, $4) RETURNING *", [name, schema, validator, meta]).then(get_object).then(save_object_to(self));
	}
	return create2;
};

/* EOF */
<commit_msg>Implemented \.test()<commit_after>/* nor-nopg */

var debug = require('nor-debug');
var Q = require('q');
var pg = require('nor-pg');
var extend = require('nor-extend').setup({useFunctionPromises:true});
var NoPgObject = require('./Object.js');
var NoPgType = require('./Type.js');
var NoPgAttachment = require('./Attachment.js');
var NoPgLib = require('./Lib.js');

/** */
function assert(valid, text) {
	if(!valid) {
		throw new TypeError(text);
	}
}

/** Assert that the `doc` is NoPgObject */
function assert_type(doc, type, text) {
	assert(doc instanceof type, text || "Not correct type: " + type);
}

/** The constructor */
function NoPG(db) {
	var self = this;
	self._db = db;
	self._values = [];
}

module.exports = NoPG;

// Object constructors
NoPG.Object = NoPgObject;

/** Start */
NoPG.start = function(pgconfig) {
	return extend.promise( [NoPG], pg.start(pgconfig).then(function(db) {
		return new NoPG(db);
	}));
};

/** Fetch next value from queue */
NoPG.prototype.fetch = function() {
	return this._values.shift();
};

/** Get object handler */
function get_object(rows) {
	var doc = rows.shift();
	if(!doc) { throw new TypeError("failed to parse object"); }
	var obj = {};
	Object.keys(doc).forEach(function(key) {
		obj['$'+key] = doc[key];
	});
	return new NoPgObject(obj);
}

/** Save object handler */
function save_object_to(self) {
	if(self instanceof NoPgObject) {
		return function(doc) {
			return self.update(doc);
		};
	}

	if(self._values) {
		return function(doc) {
			self._values.push( doc );
			return self;
		};
	}

	throw new TypeError("Unknown target");
}

/** Perform query */
function do_query(self, query, values) {
	return extend.promise( [NoPG], self._db._query(query, values) );
}

/** Commit transaction */
NoPG.prototype.commit = function() {
	return extend.promise( [NoPG], this._db.commit() );
};

/** Checks if server has compatible version */
NoPG.prototype.testServerVersion = function() {
	var self = this;
	return do_query(self, 'show server_version_num').then(function(num) {
		num = parseInt(num, 10);
		if(num < 90300) {
			throw new TypeError("PostgreSQL server must be v9.3 or newer");
		}
		return self;
	});
};

/** Checks if server has compatible version */
NoPG.prototype.testExtension = function(name) {
	var self = this;
	return do_query(self, 'select COUNT(*) AS count from pg_catalog.pg_extension where extname = $1', [name]).then(function(rows) {
		var row = rows.shift();
		var count = parseInt(row.count, 10);
		if(count === 0) {
			throw new TypeError("PostgreSQL server does not have extension: " + name);
		}
		return self;
	});
};

/** Tests if the server is compatible */
NoPG.prototype.test = function() {
	return this.testServerVersion().testExtension('plv8').testExtension('uuid-ossp').testExtension('moddatetime').testExtension('tcn');
};

/** Initialize the database */
NoPG.prototype.init = function() {
	var self = this;
	var builders = require('./schema/');
	return self.test(function() {
		return builders.reduce(function (so_far, f) {
		    return so_far.then(function(db) {
				db.fetchAll();
				return db;
			}).then(f);
		}, Q(db)).then(function() { return self; });
	});
};

/** Create object by type: `db.create([TYPE])([OPT(S)])`. */
NoPG.prototype.create = function(type) {
	debug.log('at create(', type, ')');
	var self = this;
	function create2(data) {
		debug.log('at create2(', data, ')');

		var query, params;

		if(type !== undefined) {
			if(typeof type === 'string') {
				query = "INSERT INTO objects (content, types_id) VALUES ($1, get_type($2)) RETURNING *";
				params = [data, type];
			} else if(type instanceof NoPgType) {
				query = "INSERT INTO objects (content, types_id) VALUES ($1, $2) RETURNING *";
				params = [data, type.id];
			} else {
				throw new TypeError("unknown type: " + type);
			}
		} else {
			query = "INSERT INTO objects (content) VALUES ($1) RETURNING *";
			params = [data];
		}

		return do_query(self, query, params).then(get_object).then(save_object_to(self));
	}
	return create2;
};

/** Update object */
NoPG.prototype.update = function(doc, data) {
	var self = this;
	assert_type(doc, NoPgObject, "doc is not NoPg.Object");
	return do_query(self, "UPDATE objects SET content = $1 RETURNING *", [data]).then(get_object).then(save_object_to(doc)).then(function() { return self; });
};

/** Create type object by type: `db.createType([TYPE-NAME])([OPT(S)])`. */
NoPG.prototype.createType = function(name) {
	debug.log('at createType(', name, ')');
	var self = this;
	function create2(opts) {
		opts = opts || {};
		debug.log('at createType2(', opts, ')');
		var schema = opts.schema || {};
		var validator = (''+opts.validator) || null;
		var meta = opts.meta || {};
		return do_query(self, "INSERT INTO types (name, schema, validator, meta) VALUES ($1, $2, $3, $4) RETURNING *", [name, schema, validator, meta]).then(get_object).then(save_object_to(self));
	}
	return create2;
};

/* EOF */
<|endoftext|>


<commit_before>/**
* Module that contains the linked list data structure implementations.
* Depends on core.js, datastructures.js, anim.js, utils.js
*/
/*global JSAV:true */
(function($) {
  "use strict";
  if (typeof JSAV === "undefined") { return; }

  var Edge = JSAV._types.ds.Edge;

  var List = function(jsav, options) {
    this.jsav = jsav;
    this.options = $.extend({visible: true, nodegap: 40}, options);
    var el = this.options.element || $("
");
    el.addClass("jsavlist");
    for (var key in this.options) {
      var val = this.options[key];
      if (this.options.hasOwnProperty(key) && typeof(val) === "string" || typeof(val) === "number" || typeof(val) === "boolean") {
        el.attr("data-" + key, val);
      }
    }
    if (!this.options.element) {
      $(jsav.canvas).append(el);
    }
    this.element = el;
    this.element.attr({"id": this.id()});
    JSAV.utils._helpers.handlePosition(this);
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };
  var listproto = List.prototype;
  $.extend(listproto, JSAV._types.ds.common);
  listproto.first = function(newFirst, options) {
    if (typeof newFirst === "undefined") {
      return this._first;
    } else {
      return this.addFirst(newFirst, options);
    }
  };
  listproto._setfirst = JSAV.anim(function(newFirst) {
    var oldFirst = this._first;
    this._first = newFirst;
    return [oldFirst];
  });
  listproto.add = function(index, newValue, options) {
    if (index < 0 || index > this.size()) { return this; }
    if (index === 0) {
      return this.addFirst(newValue, options);
    }
    var node = this.get(index - 1),
      newNode;
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    if (node) { // there is node for the index
      newNode.next(node.next(), options);
      node.next(newNode, options);
    }
    return this;
  };
  listproto.addFirst = function(newValue, options) {
    if (newValue instanceof ListNode) {
      newValue.next(this._first, options);
      this._setfirst(newValue, options);
    } else {
      this._setfirst(this.newNode(newValue, $.extend({}, options, {first: true, next: this._first})), options);
    }
    return this;
  };
  /** returns the last item in the list or if newLast is given, adds it to the end */
  listproto.last = function(newLast, options) {
    if (typeof newLast === "undefined") {
      var curNode = this.first();
      while (curNode.next()) {
        curNode = curNode.next();
      }
      return curNode;
    } else {
      this.addLast(newLast, options);
    }
  };
  /** adds the given value/node as the last item in the list */
  listproto.addLast = function(newValue, options) {
    var last = this.last(),
        newNode;
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    last.next(newNode, options);
    return this;
  };
  /** Returns the item at index, first node is at index 0 */
  listproto.get = function(index) {
    if (typeof(index) !== "number" || index < 0) { return; }
    var curNode = this.first(),
        pos = 0;
    while (curNode.next() && pos < index) {
      curNode = curNode.next();
      pos++;
    }
    if (pos === index) {
      return curNode;
    } else {
      return undefined;
    }
  };
  listproto.newNode = function(value, options) {
    return new ListNode(this, value, $.extend({first: false}, this.options, options));
  };
  listproto.remove = function(index, options) {
    // TODO: remove bounds checks -> use removefirst/last
    if (index === 0) {
      return this.removeFirst(options);
    } else if (index === this.size() - 1) {
      return this.removeLast(options);
    }
    var prev = this.get(index - 1),
        next = this.get(index + 1),
        oldNode = prev.next();
    prev.next(next, options);
    return oldNode;
  };
  listproto.removeFirst = function(options) {
    var oldFirst = this.first();
    this._setfirst(oldFirst.next(), options);
    return oldFirst;
  };
  listproto.removeLast = function(options) {
    var newLast = this.get(this.size() - 2),
      oldLast = this.last();
    newLast.next(null, options);
    return oldLast;
  };
  listproto.layout = function(options) {
    var layoutAlg = $.extend({}, this.options, options).layout || "_default";
    this.jsav.ds.layout.list[layoutAlg](this, options);
  };
  listproto.state = function(newState) {
    // TODO: implement list.state
  };
  listproto.size = function() {
    var curNode = this.first(),
      size = 0;
    while (curNode) {
      size++;
      curNode = curNode.next();
    }
    return size;
  };
  listproto.css = JSAV.utils._helpers.css;
  listproto._setcss = JSAV.anim(JSAV.utils._helpers._setcss);

  // add the event handler registration functions to the list prototype
  JSAV.utils._events._addEventSupport(listproto);

  var ListNode = function(container, value, options) {
    this.jsav = container.jsav;
    this.container = container;
    this._next = options.next;
    this._value = value;
    this.options = $.extend(true, {visible: true}, options);
    var el = $("
" + this._valstring(value) + "
"),
      valtype = typeof(value);
    if (valtype === "object") { valtype = "string"; }
    this.element = el;
    el.addClass("jsavnode jsavlistnode")
        .attr({"data-value": value, "id": this.id(), "data-value-type": valtype })
        .data("node", this);
    if ("first" in options && options.first) {
      this.container.element.prepend(el);
    } else {
      this.container.element.append(el);
    }
    if (this._next) {
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
      if (this.options.edgeLabel) {
        this._edgetonext.label(this.options.edgeLabel);
      }
    }
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };

  var listnodeproto = ListNode.prototype;
  $.extend(listnodeproto, JSAV._types.ds.Node.prototype);

  listnodeproto.next = function(newNext, options) {
    if (typeof newNext === "undefined") {
      return this._next;
    } else {
      return this._setnext(newNext, options);
    }
  };
  listnodeproto._setnext = JSAV.anim(function(newNext, options) {
    var oldNext = this._next;
    this._next = newNext;
    if (newNext && this._edgetonext) {
      this._edgetonext.end(newNext);
    } else if (newNext){
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
    }
    if (options && options.edgeLabel) {
      this._edgetonext.label(options.edgeLabel);
    }
    return [oldNext];
  });
  listnodeproto.edgeToNext = function() {
    return this._edgetonext;
  };
  listnodeproto.state = function(newState) {
    // TODO: implement state
  };
  function centerList(list, width, options) {
    // center the list inside its parent container
    if (list.options.hasOwnProperty("center") && !list.options.center) {
      // if options center is set to falsy value, return
      return;
    }
    // width of list expected to be last items position + its width
    var containerWidth = $(list.jsav.canvas).width();
    list.css("left", (containerWidth - width)/2, options);
  }

  var horizontalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in horizontal list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top }; // by default, don't move it
    if (opts.updateLeft) { newPos.left = prevNode?(prevPos.left +
                            prevNode.element.outerWidth() + opts.nodegap):0; }
    if (opts.updateTop) { newPos.top = 0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + prevNode.element.width() - 5,
                  prevPos.top + Math.round(prevNode.element.height()/2)],
          end = [1, newPos.left - 3,
                  newPos.top + Math.round(node.element.height()/2)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var verticalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in vertical list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top };
    if (opts.updateLeft) { newPos.left = 0; }
    if (opts.updateTop) { newPos.top = prevNode?(prevPos.top +
                          prevNode.element.outerHeight() + opts.nodegap):0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + Math.round(prevNode.element.width()/2),
                  prevPos.top + Math.round(prevNode.element.height()) + 2],
          end = [1, newPos.left + Math.round(prevNode.element.width()/2),
                  Math.round(newPos.top - 4)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var listLayout = function(list, options, updateFunc) {
    // a general list layout that goes through the nodes and calls given updateFunc
    // to calculate the new node positions
    var curNode = list.first(),
        prevNode,
        opts = $.extend({updateLeft: true, updateTop: true, updateEdges: true}, list.options, options),
        prevPos = {},
        minLeft = Number.MAX_VALUE,
        minTop = Number.MAX_VALUE,
        maxLeft = Number.MIN_VALUE,
        maxTop = Number.MIN_VALUE,
        posData = [],
        nodePos;
    // two phase layout: first go through all the nodes calculate positions
    while (curNode) {
      nodePos = updateFunc(curNode, prevNode, prevPos, opts);
      prevPos = nodePos[0];
      // keep track of max and min coordinates to calculate the size of the container
      minLeft = typeof(prevPos.left!=="undefined")?Math.min(prevPos.left, minLeft):minLeft;
      minTop = typeof(prevPos.top!=="undefined")?Math.min(prevPos.top, minTop):minTop;
      maxLeft = typeof(prevPos.left!=="undefined")?Math.max(prevPos.left + (prevNode?prevNode.element.outerWidth():0), maxLeft):maxLeft;
      maxTop = typeof(prevPos.top!=="undefined")?Math.max(prevPos.top + (prevNode?prevNode.element.outerHeight():0), maxTop):maxTop;
      posData.unshift({node: curNode, nodePos: prevPos});
      // if we also have edge position data, store that
      if (nodePos.length > 1) {
        posData[0].edgePos = nodePos[1];
        posData[0].edge = prevNode.edgeToNext();
      }
      // go to next node and continue with that
      prevNode = curNode;
      curNode = curNode.next();
    }
    var width = maxLeft - minLeft,
        height = maxTop - minTop;
    if (opts.boundsOnly) {
      return { width: width,
              height: height,
              left: list.element.position().left,
              top: list.element.position().top };
    }
    // ..update list size and position..
    list.css({width: width, height: height});
    centerList(list, width, opts);
    // .. and finally update the node and edge positions
    // doing the size first makes the animation look smoother by reducing some flicker
    for (var i = posData.length - 1; i >= 0; i--) {
      var posItem = posData[i];
      posItem.node.css(posItem.nodePos);
      if (posItem.edge) {
        posItem.edge.g.movePoints(posItem.edgePos, opts);
      }
    }
  };
  var verticalList = function(list, options) {
    list.element.addClass("jsavverticallist");
    // use the general list layout with verticalNodePosUpdate as the calculator
    return listLayout(list, options, verticalNodePosUpdate);
  };
  var horizontalList = function(list, options) {
    list.element.addClass("jsavhorizontallist");
    // use the general list layout with horizontalNodePosUpdate as the calculator
    return listLayout(list, options, horizontalNodePosUpdate);
  };

  JSAV.ext.ds.layout.list = {
    "_default": horizontalList,
    "horizontal": horizontalList,
    "vertical": verticalList
  };

  JSAV.ext.ds.list = function(options) {
    return new List(this, options);
  };
}(jQuery));<commit_msg>Expose the list types<commit_after>/**
* Module that contains the linked list data structure implementations.
* Depends on core.js, datastructures.js, anim.js, utils.js
*/
/*global JSAV:true */
(function($) {
  "use strict";
  if (typeof JSAV === "undefined") { return; }

  var Edge = JSAV._types.ds.Edge;

  var List = function(jsav, options) {
    this.jsav = jsav;
    this.options = $.extend({visible: true, nodegap: 40}, options);
    var el = this.options.element || $("
");
    el.addClass("jsavlist");
    for (var key in this.options) {
      var val = this.options[key];
      if (this.options.hasOwnProperty(key) && typeof(val) === "string" || typeof(val) === "number" || typeof(val) === "boolean") {
        el.attr("data-" + key, val);
      }
    }
    if (!this.options.element) {
      $(jsav.canvas).append(el);
    }
    this.element = el;
    this.element.attr({"id": this.id()});
    JSAV.utils._helpers.handlePosition(this);
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };
  var listproto = List.prototype;
  $.extend(listproto, JSAV._types.ds.common);
  listproto.first = function(newFirst, options) {
    if (typeof newFirst === "undefined") {
      return this._first;
    } else {
      return this.addFirst(newFirst, options);
    }
  };
  listproto._setfirst = JSAV.anim(function(newFirst) {
    var oldFirst = this._first;
    this._first = newFirst;
    return [oldFirst];
  });
  listproto.add = function(index, newValue, options) {
    if (index < 0 || index > this.size()) { return this; }
    if (index === 0) {
      return this.addFirst(newValue, options);
    }
    var node = this.get(index - 1),
      newNode;
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    if (node) { // there is node for the index
      newNode.next(node.next(), options);
      node.next(newNode, options);
    }
    return this;
  };
  listproto.addFirst = function(newValue, options) {
    if (newValue instanceof ListNode) {
      newValue.next(this._first, options);
      this._setfirst(newValue, options);
    } else {
      this._setfirst(this.newNode(newValue, $.extend({}, options, {first: true, next: this._first})), options);
    }
    return this;
  };
  /** returns the last item in the list or if newLast is given, adds it to the end */
  listproto.last = function(newLast, options) {
    if (typeof newLast === "undefined") {
      var curNode = this.first();
      while (curNode.next()) {
        curNode = curNode.next();
      }
      return curNode;
    } else {
      this.addLast(newLast, options);
    }
  };
  /** adds the given value/node as the last item in the list */
  listproto.addLast = function(newValue, options) {
    var last = this.last(),
        newNode;
    if (newValue instanceof ListNode) {
      newNode = newValue;
    } else {
      newNode = this.newNode(newValue);
    }
    last.next(newNode, options);
    return this;
  };
  /** Returns the item at index, first node is at index 0 */
  listproto.get = function(index) {
    if (typeof(index) !== "number" || index < 0) { return; }
    var curNode = this.first(),
        pos = 0;
    while (curNode.next() && pos < index) {
      curNode = curNode.next();
      pos++;
    }
    if (pos === index) {
      return curNode;
    } else {
      return undefined;
    }
  };
  listproto.newNode = function(value, options) {
    return new ListNode(this, value, $.extend({first: false}, this.options, options));
  };
  listproto.remove = function(index, options) {
    // TODO: remove bounds checks -> use removefirst/last
    if (index === 0) {
      return this.removeFirst(options);
    } else if (index === this.size() - 1) {
      return this.removeLast(options);
    }
    var prev = this.get(index - 1),
        next = this.get(index + 1),
        oldNode = prev.next();
    prev.next(next, options);
    return oldNode;
  };
  listproto.removeFirst = function(options) {
    var oldFirst = this.first();
    this._setfirst(oldFirst.next(), options);
    return oldFirst;
  };
  listproto.removeLast = function(options) {
    var newLast = this.get(this.size() - 2),
      oldLast = this.last();
    newLast.next(null, options);
    return oldLast;
  };
  listproto.layout = function(options) {
    var layoutAlg = $.extend({}, this.options, options).layout || "_default";
    this.jsav.ds.layout.list[layoutAlg](this, options);
  };
  listproto.state = function(newState) {
    // TODO: implement list.state
  };
  listproto.size = function() {
    var curNode = this.first(),
      size = 0;
    while (curNode) {
      size++;
      curNode = curNode.next();
    }
    return size;
  };
  listproto.css = JSAV.utils._helpers.css;
  listproto._setcss = JSAV.anim(JSAV.utils._helpers._setcss);

  // add the event handler registration functions to the list prototype
  JSAV.utils._events._addEventSupport(listproto);

  var ListNode = function(container, value, options) {
    this.jsav = container.jsav;
    this.container = container;
    this._next = options.next;
    this._value = value;
    this.options = $.extend(true, {visible: true}, options);
    var el = $("
" + this._valstring(value) + "
"),
      valtype = typeof(value);
    if (valtype === "object") { valtype = "string"; }
    this.element = el;
    el.addClass("jsavnode jsavlistnode")
        .attr({"data-value": value, "id": this.id(), "data-value-type": valtype })
        .data("node", this);
    if ("first" in options && options.first) {
      this.container.element.prepend(el);
    } else {
      this.container.element.append(el);
    }
    if (this._next) {
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
      if (this.options.edgeLabel) {
        this._edgetonext.label(this.options.edgeLabel);
      }
    }
    JSAV.utils._helpers.handleVisibility(this, this.options);
  };

  var listnodeproto = ListNode.prototype;
  $.extend(listnodeproto, JSAV._types.ds.Node.prototype);

  listnodeproto.next = function(newNext, options) {
    if (typeof newNext === "undefined") {
      return this._next;
    } else {
      return this._setnext(newNext, options);
    }
  };
  listnodeproto._setnext = JSAV.anim(function(newNext, options) {
    var oldNext = this._next;
    this._next = newNext;
    if (newNext && this._edgetonext) {
      this._edgetonext.end(newNext);
    } else if (newNext){
      this._edgetonext = new Edge(this.jsav, this, this._next, {"arrow-end": "classic-wide-long"});
    }
    if (options && options.edgeLabel) {
      this._edgetonext.label(options.edgeLabel);
    }
    return [oldNext];
  });
  listnodeproto.edgeToNext = function() {
    return this._edgetonext;
  };
  listnodeproto.state = function(newState) {
    // TODO: implement state
  };

  // expose the list types
  var dstypes = JSAV._types.ds;
  dstypes.List = List;
  dstypes.ListNode = ListNode;

  function centerList(list, width, options) {
    // center the list inside its parent container
    if (list.options.hasOwnProperty("center") && !list.options.center) {
      // if options center is set to falsy value, return
      return;
    }
    // width of list expected to be last items position + its width
    var containerWidth = $(list.jsav.canvas).width();
    list.css("left", (containerWidth - width)/2, options);
  }

  var horizontalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in horizontal list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top }; // by default, don't move it
    if (opts.updateLeft) { newPos.left = prevNode?(prevPos.left +
                            prevNode.element.outerWidth() + opts.nodegap):0; }
    if (opts.updateTop) { newPos.top = 0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + prevNode.element.width() - 5,
                  prevPos.top + Math.round(prevNode.element.height()/2)],
          end = [1, newPos.left - 3,
                  newPos.top + Math.round(node.element.height()/2)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var verticalNodePosUpdate = function(node, prevNode, prevPos, opts) {
    // function for calculating node positions in vertical list
    var nodePos = node.element.position(),
        newPos = { left: nodePos.left, top: nodePos.top };
    if (opts.updateLeft) { newPos.left = 0; }
    if (opts.updateTop) { newPos.top = prevNode?(prevPos.top +
                          prevNode.element.outerHeight() + opts.nodegap):0; }
    var edge = prevNode?prevNode._edgetonext:undefined;
    if (edge && opts.updateEdges) {
      var start = [0, prevPos.left + Math.round(prevNode.element.width()/2),
                  prevPos.top + Math.round(prevNode.element.height()) + 2],
          end = [1, newPos.left + Math.round(prevNode.element.width()/2),
                  Math.round(newPos.top - 4)];
      return [newPos, [start, end]];
    }
    return [newPos];
  };
  var listLayout = function(list, options, updateFunc) {
    // a general list layout that goes through the nodes and calls given updateFunc
    // to calculate the new node positions
    var curNode = list.first(),
        prevNode,
        opts = $.extend({updateLeft: true, updateTop: true, updateEdges: true}, list.options, options),
        prevPos = {},
        minLeft = Number.MAX_VALUE,
        minTop = Number.MAX_VALUE,
        maxLeft = Number.MIN_VALUE,
        maxTop = Number.MIN_VALUE,
        posData = [],
        nodePos;
    // two phase layout: first go through all the nodes calculate positions
    while (curNode) {
      nodePos = updateFunc(curNode, prevNode, prevPos, opts);
      prevPos = nodePos[0];
      // keep track of max and min coordinates to calculate the size of the container
      minLeft = typeof(prevPos.left!=="undefined")?Math.min(prevPos.left, minLeft):minLeft;
      minTop = typeof(prevPos.top!=="undefined")?Math.min(prevPos.top, minTop):minTop;
      maxLeft = typeof(prevPos.left!=="undefined")?Math.max(prevPos.left + (prevNode?prevNode.element.outerWidth():0), maxLeft):maxLeft;
      maxTop = typeof(prevPos.top!=="undefined")?Math.max(prevPos.top + (prevNode?prevNode.element.outerHeight():0), maxTop):maxTop;
      posData.unshift({node: curNode, nodePos: prevPos});
      // if we also have edge position data, store that
      if (nodePos.length > 1) {
        posData[0].edgePos = nodePos[1];
        posData[0].edge = prevNode.edgeToNext();
      }
      // go to next node and continue with that
      prevNode = curNode;
      curNode = curNode.next();
    }
    var width = maxLeft - minLeft,
        height = maxTop - minTop;
    if (opts.boundsOnly) {
      return { width: width,
              height: height,
              left: list.element.position().left,
              top: list.element.position().top };
    }
    // ..update list size and position..
    list.css({width: width, height: height});
    centerList(list, width, opts);
    // .. and finally update the node and edge positions
    // doing the size first makes the animation look smoother by reducing some flicker
    for (var i = posData.length - 1; i >= 0; i--) {
      var posItem = posData[i];
      posItem.node.css(posItem.nodePos);
      if (posItem.edge) {
        posItem.edge.g.movePoints(posItem.edgePos, opts);
      }
    }
  };
  var verticalList = function(list, options) {
    list.element.addClass("jsavverticallist");
    // use the general list layout with verticalNodePosUpdate as the calculator
    return listLayout(list, options, verticalNodePosUpdate);
  };
  var horizontalList = function(list, options) {
    list.element.addClass("jsavhorizontallist");
    // use the general list layout with horizontalNodePosUpdate as the calculator
    return listLayout(list, options, horizontalNodePosUpdate);
  };

  JSAV.ext.ds.layout.list = {
    "_default": horizontalList,
    "horizontal": horizontalList,
    "vertical": verticalList
  };

  JSAV.ext.ds.list = function(options) {
    return new List(this, options);
  };
}(jQuery));<|endoftext|>


<commit_before>// applies transition rules from design to implementation phase

const save = require('../helpers/save.js')
const bubbleTxt = require('../helpers/bubbleTxt.js')
const bubbleHTML = require('../helpers/bubbleHTML.js')

/**
 * applies transition rules from design to implementation phase
 *
 * @param {Object} cy cytoscape instance
 * @param {number} nodeCounter id counter of nodes
 */
const transition = (cy, nodeCounter) => {
  // make the changes in the node components
  cy.nodes().map(node => {
    if (node.data().asto.concept === 'micronet') {
      node.data().asto.state = ''
    } else if (node.data().asto.concept === 'device') {
      node.data().asto.layer = ''
      node.data().asto.type = ''
      node.data().asto.service = ''
      node.data().asto.input = ''
      node.data().asto.output = ''
      node.data().asto.update = ''
    } else if (node.data().asto.concept === 'application') {
      node.data().asto.version = ''
      node.data().asto.input = ''
      node.data().asto.output = ''
      node.data().asto.update = ''
    } else if (node.data().asto.concept === 'information') {
      node.data().asto.location = ''
    }
  })
  /** add connections between devices */
  cy.edges().map(edge => {
    if (edge.data().label === 'connects') {
      const source = edge.data().source
      const target = edge.data().target

      // console.log(nodeCounter)
      nodeCounter += 1 // used for the id of the node
      // add the connection node
      cy.add({
        group: 'nodes',
        data: {
          id: `n${nodeCounter}`,
          label: 'connection',
          asto: {
            description: '',
            medium: '',
            listOfProtocols: '',
            concept: 'connection'
          }
        }
      })

      // change the properties of the existing connection
      // to link the connection the first device
      edge.data().target = source
      edge.data().source = `n${nodeCounter}`
      edge.data().id = `en${nodeCounter}${source}`
      console.log(edge.data().id)

      // create an edge to link the connection to the other device
      cy.add({
        group: 'edges',
        data: {
          id: `en${nodeCounter}${target}`,
          source: `n${nodeCounter}`,
          target: `${target}`,
          label: 'connects'
        }
      })
    }
  })
}

const userInput = `the current model will stop working\n\ndo you want to continue? <button id='yes-button' class='menu-button' style='color: var(--main-tx-color); background-color: var(--main-bg-color); width: 45px; height: 25px;'>yes</button> <button id='no-button' class='menu-button' style='color: var(--main-tx-color); background-color: var(--main-bg-color); width: 45px; height: 25px;'>no</button>`

const warning =
  'to access the new implementation phase model you will need to manually load it'

/**
 * uses the transition rules and saves the graph
 *
 * @param {Object} cy cytoscape instance
 * @param {number} nodeCounter id counter of nodes
 */
module.exports = function dgnState2ImpState (cy, nodeCounter) {
  bubbleHTML(userInput)

  // create a `yes` button that will perform the transition
  const buttonYes = document.getElementById('yes-button')
  buttonYes.addEventListener('click', () => {
    transition(cy, nodeCounter)
    save(cy)
    bubbleTxt('model transition successful')
    bubbleTxt(warning)
  })
  // create a `no` button that will clear the message bubble
  const buttonNo = document.getElementById('no-button')
  buttonNo.addEventListener('click', () => {
    document.getElementById('message-area-id').removeChild(buttonNo.parentNode)
  })
}
<commit_msg>remove unused comments<commit_after>// applies transition rules from design to implementation phase

const save = require('../helpers/save.js')
const bubbleTxt = require('../helpers/bubbleTxt.js')
const bubbleHTML = require('../helpers/bubbleHTML.js')

/**
 * applies transition rules from design to implementation phase
 *
 * @param {object} cy cytoscape instance
 * @param {number} nodeCounter id counter of nodes
 */
const transition = (cy, nodeCounter) => {
  // make the changes in the node components
  cy.nodes().map(node => {
    if (node.data().asto.concept === 'micronet') {
      node.data().asto.state = ''
    } else if (node.data().asto.concept === 'device') {
      node.data().asto.layer = ''
      node.data().asto.type = ''
      node.data().asto.service = ''
      node.data().asto.input = ''
      node.data().asto.output = ''
      node.data().asto.update = ''
    } else if (node.data().asto.concept === 'application') {
      node.data().asto.version = ''
      node.data().asto.input = ''
      node.data().asto.output = ''
      node.data().asto.update = ''
    } else if (node.data().asto.concept === 'information') {
      node.data().asto.location = ''
    }
  })

  /** add connections between devices */
  cy.edges().map(edge => {
    if (edge.data().label === 'connects') {
      const source = edge.data().source
      const target = edge.data().target

      nodeCounter += 1 // used for the id of the node
      // add the connection node
      cy.add({
        group: 'nodes',
        data: {
          id: `n${nodeCounter}`,
          label: 'connection',
          asto: {
            description: '',
            medium: '',
            listOfProtocols: '',
            concept: 'connection'
          }
        }
      })

      // change the properties of the existing connection
      // to link the connection the first device
      edge.data().target = source
      edge.data().source = `n${nodeCounter}`
      edge.data().id = `en${nodeCounter}${source}`
      console.log(edge.data().id)

      // create an edge to link the connection to the other device
      cy.add({
        group: 'edges',
        data: {
          id: `en${nodeCounter}${target}`,
          source: `n${nodeCounter}`,
          target: `${target}`,
          label: 'connects'
        }
      })
    }
  })
}

const userInput = `the current model will stop working\n\ndo you want to continue? <button id='yes-button' class='menu-button' style='color: var(--main-tx-color); background-color: var(--main-bg-color); width: 45px; height: 25px;'>yes</button> <button id='no-button' class='menu-button' style='color: var(--main-tx-color); background-color: var(--main-bg-color); width: 45px; height: 25px;'>no</button>`

const warning =
  'to access the new implementation phase model you will need to manually load it'

/**
 * uses the transition rules and saves the graph
 *
 * @param {object} cy cytoscape instance
 * @param {number} nodeCounter id counter of nodes
 */
module.exports = function dgnState2ImpState (cy, nodeCounter) {
  bubbleHTML(userInput)

  // create a `yes` button that will perform the transition
  const buttonYes = document.getElementById('yes-button')
  buttonYes.addEventListener('click', () => {
    transition(cy, nodeCounter)
    save(cy)
    bubbleTxt('model transition successful')
    bubbleTxt(warning)
  })
  // create a `no` button that will clear the message bubble
  const buttonNo = document.getElementById('no-button')
  buttonNo.addEventListener('click', () => {
    document.getElementById('message-area-id').removeChild(buttonNo.parentNode)
  })
}
<|endoftext|>


<commit_before>/*
Author: Ben Brooks Scholz (c) 2011

list.js provides the functions to create a hashed key-value list.

The global constant MAX_ELEMENTS determines the initialized list
size. This value should be changed based on the size of your data
set.

This code is licensed under the GPLv3.

*/

// maximum elements for all lists
var MAX_ELEMENTS = 500;
// prime multiplier for hash function
var HASH_MULTIPLIER = 37;

/**
 * @constructor
 * List allows to access elements through a key (string of characters).
 * It operates like a hash table: it is an associative array with the
 * values chained in a linked list to deal with hash collisions.
 */
var List = function () {
    this.h_list = [];
    this.h_list.length = MAX_ELEMENTS;
};

/**
 * List functions to create and manipulate a hashed list.
 */
List.prototype = {

    /**
     * Entry consists of a key, value, and a link to the next entry.
     */
    Entry: function (k, v) {
        this.key = k;
        this.val = v;
        this.next = undefined;
    },

    /**
     * get: returns the value attached to the given key or undefined if it isn't found.
     */
    get: function(key) {
        var h, nextentry;
        // hash the key
        h = this.hash(key);
        // if there is a missing value return undefined
        if (this.h_list[h] === undefined)
            return undefined;
        nextentry = this.h_list[h];
        // iterate through the entries with the hash
        while(nextentry !== undefined) {
            if (nextentry.key === key)
                return nextentry.val;
            nextentry = nextentry.next;
        }
        // nothing found
        return undefined;
    },

    /**
     * add: inserts an object into the list, assigning it to the given key.
     * It returns the value upon successful addition to the list. In the
     * event of hash collisions, the entry is chained to end of the value
     * already in the hash's cell. If a value is inserted with a key that
     * exists in the list already, the old value is overwritten.
     */
    add: function (key, value) {
        var h, entry, nextentry;
        // create new entry with the key and value, hash the key
        entry = new this.Entry(key, value);
        h = this.hash(key);
        // if the hash corresponds to nothing, add the entry
        if (this.h_list[h] === undefined) {
            this.h_list[h] = entry;
            return this.h_list[h].val;
        } else {
            nextentry = this.h_list[h];
            // iterate to the last item at that hash value in the list
            while (nextentry.next !== undefined) {
                if (nextentry.key === key) {
                    // overwrite entry with the same key
                    nextentry.val = value;
                    return nextentry.val;
                }
                nextentry = nextentry.next;
            }
            nextentry.next = entry;
            return nextentry.next.val;
        }
    },

    /**
     * remove: removes an object from the list with the given key. It returns
     * false if no object with the given key exists in the list. Otherwise, it
     * returns the value removed.
     */
    remove: function (key) {
        var h, current, next;
        h = this.hash(key);
        // if the hash doesn't correspong to anything
        if (this.h_list[h] === undefined)
            return false;
        else {
            next = this.h_list[h];
            // iterate through the entries with the hash
            while (next !== undefined) {
                if (next.key === key)
                    // if the key matches the first cell
                    // connect h_list[h] to the next value
                    if (current === undefined) {
                        this.h_list[h] = next.next;
                        // the value removed
                        return next.val;
                    // remove by chaining previous entry to next entry
                    } else {
                        current.next = next.next;
                        // the value removed
                        return next.val;
                    }
                // step forward in the chain
                current = next;
                next = next.next;
            }
            // nothing found
            return false;
        }
    },

    /**
     * hash: hashes the given key and returns it.
     */
    hash: function (key) {
        var i, h;
        // iterate through the characters in the key string.
        // compute the hash using the character codes and
        // hash multiplier prime constant.
        for (i = 0; i < key.length; i = i + 1)
            h = (HASH_MULTIPLIER * h) + key.charCodeAt();
        return h % MAX_ELEMENTS;
    }
};

window['List'] = List;
List.prototype['get'] = List.prototype.get;
List.prototype['add'] = List.prototype.add;
List.prototype['remove'] = List.prototype.remove;<commit_msg>expanded comments<commit_after>/*
Author: Ben Brooks Scholz (c) 2011

list.js provides the functions to create a hashed key-value list.

The global constant MAX_ELEMENTS determines the initialized list
size. This value should be changed based on the size of your data
set.

This code is licensed under the GPLv3.

*/

// maximum elements for all lists
var MAX_ELEMENTS = 500;
// prime multiplier for hash function
var HASH_MULTIPLIER = 37;

/**
 * @constructor
 * List allows to access elements through a key (string of characters).
 * It operates like a hash table: it is an associative array with the
 * values chained in a linked list to deal with hash collisions.
 */
var List = function () {
    this.h_list = [];
    this.h_list.length = MAX_ELEMENTS;
};

/**
 * List functions to create and manipulate a hashed list.
 */
List.prototype = {

    /**
     * Entry consists of a key, value, and a link to the next entry.
     */
    Entry: function (k, v) {
        this.key = k;
        this.val = v;
        this.next = undefined;
    },

    /**
     * get: returns the value attached to the given key or undefined if it isn't found.
     */
    get: function(key) {
        var h, next;
        // hash the key
        h = this.hash(key);
        // if there is a missing value return undefined
        if (this.h_list[h] === undefined)
            return undefined;
        next = this.h_list[h];
        // iterate through the entries with the hash
        while(next !== undefined) {
            if (next.key === key)
                return next.val;
            next = next.next;
        }
        // nothing found
        return undefined;
    },

    /**
     * add: inserts an object into the list, assigning it to the given key.
     * It returns the value upon successful addition to the list. In the
     * event of hash collisions, the entry is chained to end of the value
     * already in the hash's cell. If a value is inserted with a key that
     * exists in the list already, the old value is overwritten.
     */
    add: function (key, value) {
        var h, entry, next;
        // create new entry with the key and value, hash the key
        entry = new this.Entry(key, value);
        h = this.hash(key);
        // if the hash corresponds to nothing, add the entry
        if (this.h_list[h] === undefined) {
            this.h_list[h] = entry;
            return this.h_list[h].val;
        } else {
            next = this.h_list[h];
            // iterate through the items in the list, overwriting any value with
            // the input key. If the end of the list is reached and no matching
            // key is found, append the entry to the end of the chain.
            while (next !== undefined) {
                if (next.key === key) {
                    next.val = value;
                    return next.val;
                } else if (next.next === undefined) {
                    next.next = entry;
                    return next.next.val;
                }
                next = next.next;
            }
        }
    },

    /**
     * remove: removes an object from the list with the given key. It returns
     * false if no object with the given key exists in the list. Otherwise, it
     * returns the value removed.
     */
    remove: function (key) {
        var h, current, next;
        h = this.hash(key);
        // if the hash doesn't correspong to anything
        if (this.h_list[h] === undefined)
            return false;
        else {
            next = this.h_list[h];
            // iterate through the entries with the hash
            while (next !== undefined) {
                if (next.key === key)
                    // if the key matches the first cell
                    // connect h_list[h] to the next value
                    if (current === undefined) {
                        this.h_list[h] = next.next;
                        // the value removed
                        return next.val;
                    // remove by chaining previous entry to next entry
                    } else {
                        current.next = next.next;
                        // the value removed
                        return next.val;
                    }
                // step forward in the chain
                current = next;
                next = next.next;
            }
            // nothing found
            return false;
        }
    },

    /**
     * hash: Hashes the given key and returns it. The key should be
     * a character string, as the character codes are used to compute
     * the hash. Returns false if the key is not a string.
     */
    hash: function (key) {
        var i, h;
        h = 0;
        // iterate through the characters in the key string.
        // compute the hash using the character codes and
        // hash multiplier prime constant.
        for (i = 0; i < key.length; i = i + 1)
            h = (HASH_MULTIPLIER * h) + key.charCodeAt(i);
        return h % MAX_ELEMENTS;
    }
};

window['List'] = List;
List.prototype['get'] = List.prototype.get;
List.prototype['add'] = List.prototype.add;
List.prototype['remove'] = List.prototype.remove;<|endoftext|>