<commit_before>/*************************************************
 * Copyright (c) 2015 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/

   /**
 * @ngdoc function
 * @name helpers.function:Lookup
 * @description
 *  LookUpInit( {
 *      scope: <caller's scope>,
 *      form: <form object>,
 *      current_item: <id of item to select on open>,
 *      list: <list object>,
 *      field: <name of the form field with which the lookup is associated>,
 *      hdr: <optional. modal dialog header>
 *      postAction: optional function to run after selection made,
 *      callback: optional label to $emit() on parent scope
 *      input_type: optional string that specifies whether lookup should have checkboxes or radio buttons. defaults to 'checkbox' --- added by JT 8/21/14
 *  })
 */

import listGenerator from '../shared/list-generator/main';

export default
    angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ApiLoader', 'ModalDialog'])

        .factory('LookUpInit', ['Alert', 'Rest', 'ProcessErrors', 'generateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', 'CreateDialog',
        function (Alert, Rest, ProcessErrors, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty, CreateDialog) {
            return function (params) {

                var parent_scope = params.scope,
                    form = params.form,
                    list = params.list,
                    field = params.field,
                    instructions = params.instructions,
                    postAction = params.postAction,
                    callback = params.callback,
                    autopopulateLookup,
                    input_type = (params.input_type) ? params.input_type: "checkbox",
                    defaultUrl, name, watchUrl;

                if (params.autopopulateLookup !== undefined) {
                    autopopulateLookup = params.autopopulateLookup;
                } else {
                    autopopulateLookup = true;
                }

                if (params.url) {
                    // pass in a url value to override the default
                    defaultUrl = params.url;
                } else {
                    defaultUrl = (list.name === 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name);
                }

                if ($('#htmlTemplate #lookup-modal-dialog').length > 0) {
                    $('#htmlTemplate #lookup-modal-dialog').empty();
                }
                else {
                    $('#htmlTemplate').append("

");
                }

                name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);

                watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&';
                watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value';

                $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
                $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field);

                var sourceModel = form.fields[field].sourceModel,
                    sourceField = form.fields[field].sourceField;

                // this logic makes sure that the form is being added, and that the lookup to be autopopulated is required
                // we also look to see if the lookupinit autopopulateLookup parameter passed in by the controller is false
                function fieldIsAutopopulatable() {
                    if (autopopulateLookup === false) {
                        return false;
                    } if (parent_scope.mode === "add") {
                        if (parent_scope[sourceModel + "_field"].awRequiredWhen &&
                            parent_scope[sourceModel + "_field"].awRequiredWhen.variable &&
                            parent_scope[parent_scope[sourceModel + "_field"].awRequiredWhen.variable]) {
                            return true;
                        } else if (parent_scope[sourceModel + "_field"].addRequired === true) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }

                if (fieldIsAutopopulatable()) {
                    // Auto populate the field if there is only one result
                    Rest.setUrl(defaultUrl);
                    Rest.get()
                        .success(function (data) {
                             if (data.count === 1) {
                                 parent_scope[field] = data.results[0].id;
                                 if (parent_scope[form.name + '_form'] && form.fields[field] && sourceModel) {
                                     parent_scope[sourceModel + '_' + sourceField] =
                                        data.results[0][sourceField];
                                     if (parent_scope[form.name + '_form'][sourceModel + '_' + sourceField]) {
                                         parent_scope[form.name + '_form'][sourceModel + '_' + sourceField]
                                             .$setValidity('awlookup', true);
                                     }
                                 }
                                 if (parent_scope[form.name + '_form']) {
                                     parent_scope[form.name + '_form'].$setDirty();
                                 }
                             }
                        })
                        .error(function (data, status) {
                            ProcessErrors(parent_scope, data, status, form, { hdr: 'Error!',
                                msg: 'Failed to launch adhoc command. POST returned status: ' +
                                    status });
                        });
                }

                parent_scope['lookUp' + name] = function () {

                    var master = {},
                        scope = parent_scope.$new(),
                        name, hdr, buttons;

                    // Generating the search list potentially kills the values held in scope for the field.
                    // We'll keep a copy in master{} that we can revert back to on cancel;
                    master[field] = scope[field];
                    master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
                        scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField];

                    GenerateList.inject(list, {
                        mode: 'lookup',
                        id: 'lookup-modal-dialog',
                        scope: scope,
                        instructions: instructions,
                        input_type: input_type
                    });

                    name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);
                    hdr = (params.hdr) ? params.hdr : 'Select ' + name;

                    // Show pop-up
                    buttons = [{
                        label: "Cancel",
                        icon: "fa-times",
                        "class": "btn btn-default",
                        "id": "lookup-cancel-button",
                        onClick: function() {
                            $('#lookup-modal-dialog').dialog('close');
                        }
                    },{
                        label: "Select",
                        onClick: function() {
                            scope.selectAction();
                        },
                        icon: "fa-check",
                        "class": "btn btn-primary",
                        "id": "lookup-save-button"
                    }];

                    if (scope.removeModalReady) {
                        scope.removeModalReady();
                    }
                    scope.removeModalReady = scope.$on('ModalReady', function() {
                        $('#lookup-save-button').attr('disabled','disabled');
                        $('#lookup-modal-dialog').dialog('open');
                    });

                    CreateDialog({
                        scope: scope,
                        buttons: buttons,
                        width: 600,
                        height: (instructions) ? 625 : 500,
                        minWidth: 500,
                        title: hdr,
                        id: 'lookup-modal-dialog',
                        onClose: function() {
                            setTimeout( function() {
                                scope.$apply( function() {
                                    if (Empty(scope[field])) {
                                        scope[field] = master[field];
                                        scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
                                        master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField];
                                    }
                                });
                            }, 300);
                        },
                        callback: 'ModalReady'
                    });

                    SearchInit({
                        scope: scope,
                        set: list.name,
                        list: list,
                        url: defaultUrl
                    });

                    PaginateInit({
                        scope: scope,
                        list: list,
                        url: defaultUrl,
                        mode: 'lookup'
                    });

                    if (scope.lookupPostRefreshRemove) {
                        scope.lookupPostRefreshRemove();
                    }
                    scope.lookupPostRefreshRemove = scope.$on('PostRefresh', function () {
                        var fld, i;
                        for (fld in list.fields) {
                            if (list.fields[fld].type && list.fields[fld].type === 'date') {
                                //convert dates to our standard format
                                for (i = 0; i < scope[list.name].length; i++) {
                                    scope[list.name][i][fld] = FormatDate(new Date(scope[list.name][i][fld]));
                                }
                            }
                        }

                        // List generator creates the list, resetting it and losing the previously selected value.
                        // If the selected value is in the current set, find it and mark selected.
                        if (!Empty(parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField])) {
                            scope[list.name].forEach(function(elem) {
                                if (elem[form.fields[field].sourceField] ===
                                    parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
                                    scope[field] = elem.id;
                                }
                            });

                        }

                        if (!Empty(scope[field])) {
                            scope['toggle_' + list.iterator](scope[field]);
                        }

                    });

                    scope.search(list.iterator);

                    scope.selectAction = function () {
                        var i, found = false;
                        for (i = 0; i < scope[list.name].length; i++) {
                            if (scope[list.name][i].checked === '1' || scope[list.name][i].checked===1) {
                                found = true;
                                parent_scope[field] = scope[list.name][i].id;
                                if (parent_scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) {
                                    parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
                                        scope[list.name][i][form.fields[field].sourceField];
                                    if (parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
                                        parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]
                                            .$setValidity('awlookup', true);
                                    }
                                }
                                if (parent_scope[form.name + '_form']) {
                                    parent_scope[form.name + '_form'].$setDirty();
                                }
                            }
                        }
                        if (found) {
                            // Selection made
                            $('#lookup-modal-dialog').dialog('close');
                            if (postAction) {
                                postAction();
                            }
                            if (callback) {
                                parent_scope.$emit(callback);
                            }
                        }
                    };

                    scope['toggle_' + list.iterator] = function (id) {
                        var count = 0;
                        scope[list.name].forEach( function(row, i) {
                            if (row.id === id) {
                                if (row.checked) {
                                    scope[list.name][i].success_class = 'success';
                                }
                                else {
                                    row.checked = true;
                                    scope[list.name][i].success_class = '';
                                }
                            } else {
                                scope[list.name][i].checked = 0;
                                scope[list.name][i].success_class = '';
                            }
                        });
                        // Check if any rows are checked
                        scope[list.name].forEach(function(row) {
                            if (row.checked) {
                                count++;
                            }
                        });
                        if (count === 0) {
                            $('#lookup-save-button').attr('disabled','disabled');
                        }
                        else {
                            $('#lookup-save-button').removeAttr('disabled');
                        }
                    };
                };
            };
        }]);
<commit_msg>Fix issue with lookup "Select" button<commit_after>/*************************************************
 * Copyright (c) 2015 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/

   /**
 * @ngdoc function
 * @name helpers.function:Lookup
 * @description
 *  LookUpInit( {
 *      scope: <caller's scope>,
 *      form: <form object>,
 *      current_item: <id of item to select on open>,
 *      list: <list object>,
 *      field: <name of the form field with which the lookup is associated>,
 *      hdr: <optional. modal dialog header>
 *      postAction: optional function to run after selection made,
 *      callback: optional label to $emit() on parent scope
 *      input_type: optional string that specifies whether lookup should have checkboxes or radio buttons. defaults to 'checkbox' --- added by JT 8/21/14
 *  })
 */

import listGenerator from '../shared/list-generator/main';

export default
    angular.module('LookUpHelper', ['RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'ApiLoader', 'ModalDialog'])

        .factory('LookUpInit', ['Alert', 'Rest', 'ProcessErrors', 'generateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', 'CreateDialog',
        function (Alert, Rest, ProcessErrors, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty, CreateDialog) {
            return function (params) {

                var parent_scope = params.scope,
                    form = params.form,
                    list = params.list,
                    field = params.field,
                    instructions = params.instructions,
                    postAction = params.postAction,
                    callback = params.callback,
                    autopopulateLookup,
                    input_type = (params.input_type) ? params.input_type: "checkbox",
                    defaultUrl, name, watchUrl;

                if (params.autopopulateLookup !== undefined) {
                    autopopulateLookup = params.autopopulateLookup;
                } else {
                    autopopulateLookup = true;
                }

                if (params.url) {
                    // pass in a url value to override the default
                    defaultUrl = params.url;
                } else {
                    defaultUrl = (list.name === 'inventories') ? GetBasePath('inventory') : GetBasePath(list.name);
                }

                if ($('#htmlTemplate #lookup-modal-dialog').length > 0) {
                    $('#htmlTemplate #lookup-modal-dialog').empty();
                }
                else {
                    $('#htmlTemplate').append("

");
                }

                name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);

                watchUrl = (/\/$/.test(defaultUrl)) ? defaultUrl + '?' : defaultUrl + '&';
                watchUrl += form.fields[field].sourceField + '__' + 'iexact=:value';

                $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-url', watchUrl);
                $('input[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').attr('data-source', field);

                var sourceModel = form.fields[field].sourceModel,
                    sourceField = form.fields[field].sourceField;

                // this logic makes sure that the form is being added, and that the lookup to be autopopulated is required
                // we also look to see if the lookupinit autopopulateLookup parameter passed in by the controller is false
                function fieldIsAutopopulatable() {
                    if (autopopulateLookup === false) {
                        return false;
                    } if (parent_scope.mode === "add") {
                        if (parent_scope[sourceModel + "_field"].awRequiredWhen &&
                            parent_scope[sourceModel + "_field"].awRequiredWhen.variable &&
                            parent_scope[parent_scope[sourceModel + "_field"].awRequiredWhen.variable]) {
                            return true;
                        } else if (parent_scope[sourceModel + "_field"].addRequired === true) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }

                if (fieldIsAutopopulatable()) {
                    // Auto populate the field if there is only one result
                    Rest.setUrl(defaultUrl);
                    Rest.get()
                        .success(function (data) {
                             if (data.count === 1) {
                                 parent_scope[field] = data.results[0].id;
                                 if (parent_scope[form.name + '_form'] && form.fields[field] && sourceModel) {
                                     parent_scope[sourceModel + '_' + sourceField] =
                                        data.results[0][sourceField];
                                     if (parent_scope[form.name + '_form'][sourceModel + '_' + sourceField]) {
                                         parent_scope[form.name + '_form'][sourceModel + '_' + sourceField]
                                             .$setValidity('awlookup', true);
                                     }
                                 }
                                 if (parent_scope[form.name + '_form']) {
                                     parent_scope[form.name + '_form'].$setDirty();
                                 }
                             }
                        })
                        .error(function (data, status) {
                            ProcessErrors(parent_scope, data, status, form, { hdr: 'Error!',
                                msg: 'Failed to launch adhoc command. POST returned status: ' +
                                    status });
                        });
                }

                parent_scope['lookUp' + name] = function () {

                    var master = {},
                        scope = parent_scope.$new(),
                        name, hdr, buttons;

                    // Generating the search list potentially kills the values held in scope for the field.
                    // We'll keep a copy in master{} that we can revert back to on cancel;
                    master[field] = scope[field];
                    master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
                        scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField];

                    GenerateList.inject(list, {
                        mode: 'lookup',
                        id: 'lookup-modal-dialog',
                        scope: scope,
                        instructions: instructions,
                        input_type: input_type
                    });

                    name = list.iterator.charAt(0).toUpperCase() + list.iterator.substring(1);
                    hdr = (params.hdr) ? params.hdr : 'Select ' + name;

                    // Show pop-up
                    buttons = [{
                        label: "Cancel",
                        icon: "fa-times",
                        "class": "btn btn-default",
                        "id": "lookup-cancel-button",
                        onClick: function() {
                            $('#lookup-modal-dialog').dialog('close');
                        }
                    },{
                        label: "Select",
                        onClick: function() {
                            scope.selectAction();
                        },
                        icon: "fa-check",
                        "class": "btn btn-primary",
                        "id": "lookup-save-button"
                    }];

                    if (scope.removeModalReady) {
                        scope.removeModalReady();
                    }
                    scope.removeModalReady = scope.$on('ModalReady', function() {
                        $('#lookup-save-button').attr('disabled','disabled');
                        $('#lookup-modal-dialog').dialog('open');
                    });

                    CreateDialog({
                        scope: scope,
                        buttons: buttons,
                        width: 600,
                        height: (instructions) ? 625 : 500,
                        minWidth: 500,
                        title: hdr,
                        id: 'lookup-modal-dialog',
                        onClose: function() {
                            setTimeout( function() {
                                scope.$apply( function() {
                                    if (Empty(scope[field])) {
                                        scope[field] = master[field];
                                        scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
                                        master[form.fields[field].sourceModel + '_' + form.fields[field].sourceField];
                                    }
                                });
                            }, 300);
                        },
                        callback: 'ModalReady'
                    });

                    SearchInit({
                        scope: scope,
                        set: list.name,
                        list: list,
                        url: defaultUrl
                    });

                    PaginateInit({
                        scope: scope,
                        list: list,
                        url: defaultUrl,
                        mode: 'lookup'
                    });

                    if (scope.lookupPostRefreshRemove) {
                        scope.lookupPostRefreshRemove();
                    }
                    scope.lookupPostRefreshRemove = scope.$on('PostRefresh', function () {
                        var fld, i;
                        for (fld in list.fields) {
                            if (list.fields[fld].type && list.fields[fld].type === 'date') {
                                //convert dates to our standard format
                                for (i = 0; i < scope[list.name].length; i++) {
                                    scope[list.name][i][fld] = FormatDate(new Date(scope[list.name][i][fld]));
                                }
                            }
                        }

                        // List generator creates the list, resetting it and losing the previously selected value.
                        // If the selected value is in the current set, find it and mark selected.
                        if (!Empty(parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField])) {
                            scope[list.name].forEach(function(elem) {
                                if (elem[form.fields[field].sourceField] ===
                                    parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
                                    scope[field] = elem.id;
                                }
                            });

                        }

                        if (!Empty(scope[field])) {
                            scope['toggle_' + list.iterator](scope[field]);
                        }

                    });

                    scope.search(list.iterator);

                    scope.selectAction = function () {
                        var i, found = false;
                        for (i = 0; i < scope[list.name].length; i++) {
                            if (scope[list.name][i].checked === '1' || scope[list.name][i].checked===1 || scope[list.name][i].checked === true) {
                                found = true;
                                parent_scope[field] = scope[list.name][i].id;
                                if (parent_scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) {
                                    parent_scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField] =
                                        scope[list.name][i][form.fields[field].sourceField];
                                    if (parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]) {
                                        parent_scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField]
                                            .$setValidity('awlookup', true);
                                    }
                                }
                                if (parent_scope[form.name + '_form']) {
                                    parent_scope[form.name + '_form'].$setDirty();
                                }
                            }
                        }
                        if (found) {
                            // Selection made
                            $('#lookup-modal-dialog').dialog('close');
                            if (postAction) {
                                postAction();
                            }
                            if (callback) {
                                parent_scope.$emit(callback);
                            }
                        }
                    };

                    scope['toggle_' + list.iterator] = function (id) {
                        var count = 0;
                        scope[list.name].forEach( function(row, i) {
                            if (row.id === id) {
                                if (row.checked) {
                                    scope[list.name][i].success_class = 'success';
                                }
                                else {
                                    row.checked = true;
                                    scope[list.name][i].success_class = '';
                                }
                            } else {
                                scope[list.name][i].checked = 0;
                                scope[list.name][i].success_class = '';
                            }
                        });
                        // Check if any rows are checked
                        scope[list.name].forEach(function(row) {
                            if (row.checked) {
                                count++;
                            }
                        });
                        if (count === 0) {
                            $('#lookup-save-button').attr('disabled','disabled');
                        }
                        else {
                            $('#lookup-save-button').removeAttr('disabled');
                        }
                    };
                };
            };
        }]);
<|endoftext|>


<commit_before>/* eslint-disable */

// a butchered version of https://www.warcraftlogs.com:443/v1/zones
// only inclused the raids from Legion (showing older logs wouldn't make sense)

// TODO: Refactor this (it's kind of strange and feels misplaced)

const ZONES = [
  {
    "id": 19,
    "name": "Uldir",
    "encounters": [
      {
        "id": 2144,
        "name": "Taloc",
        "npcID": 137119
      },
      {
        "id": 2141,
        "name": "MOTHER",
        "npcID": 135452
      },
      {
        "id": 2128,
        "name": "Fetid Devourer",
        "npcID": 133298
      },
      {
        "id": 2136,
        "name": "Zek'voz, Herald of N'zoth",
        "npcID": 134445
      },
      {
        "id": 2134,
        "name": "Vectis",
        "npcID": 134442
      },
      {
        "id": 2145,
        "name": "Zul, Reborn",
        "npcID": 138967
      },
      {
        "id": 2135,
        "name": "Mythrax the Unraveler",
        "npcID": 134546
      },
      {
        "id": 2122,
        "name": "G'huun",
        "npcID": 132998
      }
    ],
  },
];

export default ZONES;
<commit_msg>adjust Zul, Mythrax and Zek'vos names<commit_after>/* eslint-disable */

// a butchered version of https://www.warcraftlogs.com:443/v1/zones
// only inclused the raids from Legion (showing older logs wouldn't make sense)

// TODO: Refactor this (it's kind of strange and feels misplaced)

const ZONES = [
  {
    "id": 19,
    "name": "Uldir",
    "encounters": [
      {
        "id": 2144,
        "name": "Taloc",
        "npcID": 137119
      },
      {
        "id": 2141,
        "name": "MOTHER",
        "npcID": 135452
      },
      {
        "id": 2128,
        "name": "Fetid Devourer",
        "npcID": 133298
      },
      {
        "id": 2136,
        "name": "Zek'voz",
        "npcID": 134445
      },
      {
        "id": 2134,
        "name": "Vectis",
        "npcID": 134442
      },
      {
        "id": 2145,
        "name": "Zul",
        "npcID": 138967
      },
      {
        "id": 2135,
        "name": "Mythrax",
        "npcID": 134546
      },
      {
        "id": 2122,
        "name": "G'huun",
        "npcID": 132998
      }
    ],
  },
];

export default ZONES;
<|endoftext|>


<commit_before>module.exports = function(r) {
    'use strict';
    let run = require('./run');
    let getSingle = require('./get-single');
    return {
        forUser: forUser,
        get: function(id, index) {
            return getSingle(r, 'projects', id, index);
        }
    };

    ///////////////

    // forUser returns all the projects for a specific user. It handles
    // the case where a user is an administrator.
    function forUser(user) {
        let rql;
        if (user.isAdmin) {
            rql = r.table('projects')
                .filter(r.row('owner').ne('delete@materialscommons.org'));
        } else {
            rql = r.table('access').getAll(user.id, {index: 'user_id'})
                .eqJoin('project_id', r.table('projects'))
                .zip();
        }

        rql = transformDates(rql);
        rql = addComputed(rql);

        return run(rql);
    }

    // transformDates removes the rethinkdb specific date
    // fields
    function transformDates(rql) {
        rql = rql.merge(function(project) {
            return {
                mtime: project('mtime').toEpochTime(),
                birthtime: project('birthtime').toEpochTime()
            };
        });
        return rql;
    }

    // addComputed adds additional attributes to the rql that
    // that are computed from other tables.
    function addComputed(rql) {
        rql = rql.merge(function(project) {
            return {
                users: r.table('access')
                    .getAll(project('id'), {index: 'project_id'})
                    .map(function(entry) {
                        return entry.merge({
                            'user': entry('user_id')
                        });
                    })
                    .pluck('user', 'permissions')
                    .coerceTo('array'),
                reviews: r.table('reviews')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                notes: r.table('notes')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                events: r.table('events')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                processes: [],
                samples: [],
                drafts: []
            };
        });

        return rql;
    }

};
<commit_msg>Remove extra spaces.<commit_after>module.exports = function(r) {
    'use strict';
    let run = require('./run');
    let getSingle = require('./get-single');
    return {
        forUser: forUser,
        get: function(id, index) {
            return getSingle(r, 'projects', id, index);
        }
    };

    ///////////////

    // forUser returns all the projects for a specific user. It handles
    // the case where a user is an administrator.
    function forUser(user) {
        let rql;
        if (user.isAdmin) {
            rql = r.table('projects')
                .filter(r.row('owner').ne('delete@materialscommons.org'));
        } else {
            rql = r.table('access').getAll(user.id, {index: 'user_id'})
                .eqJoin('project_id', r.table('projects'))
                .zip();
        }

        rql = transformDates(rql);
        rql = addComputed(rql);

        return run(rql);
    }

    // transformDates removes the rethinkdb specific date
    // fields
    function transformDates(rql) {
        rql = rql.merge(function(project) {
            return {
                mtime: project('mtime').toEpochTime(),
                birthtime: project('birthtime').toEpochTime()
            };
        });
        return rql;
    }

    // addComputed adds additional attributes to the rql that
    // that are computed from other tables.
    function addComputed(rql) {
        rql = rql.merge(function(project) {
            return {
                users: r.table('access')
                    .getAll(project('id'), {index: 'project_id'})
                    .map(function(entry) {
                        return entry.merge({
                            'user': entry('user_id')
                        });
                    })
                    .pluck('user', 'permissions')
                    .coerceTo('array'),
                reviews: r.table('reviews')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                notes: r.table('notes')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                events: r.table('events')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                processes: [],
                samples: [],
                drafts: []
            };
        });

        return rql;
    }
};
<|endoftext|>


<commit_before>module.exports = function(r) {
    'use strict';
    return {
        getProjectsForUser: getProjectsForUser
    };

    ///////////////

    function *getProjectsForUser(user, isAdmin) {
        let rql;
        if (isAdmin) {
            rql = r.table('projects')
                .filter(r.row('owner').ne('delete@materialscommons.org'))
                .pluck('name').orderBy('name');
        } else {
            rql = r.table('access').getAll(user, {index: 'user_id'})
                .eqJoin('project_id', r.table('projects'))
                .zip()
                .pluck('name')
                .orderBy('name');
        }

        return yield rql.run();
    }
};
<commit_msg>Return a promise rather than a generator since you can always pass a promise to a generator. Rely on module name (golang style) for method names. The module makes the method name more understandable. General cleanup of the interface.<commit_after>module.exports = function(r) {
    'use strict';
    let run = require('./run');
    return {
        forUser: forUser
    };

    ///////////////

    function forUser(user) {
        let rql;
        if (user.isAdmin) {
            rql = r.table('projects')
                .filter(r.row('owner').ne('delete@materialscommons.org'));
        } else {
            rql = r.table('access').getAll(user.id, {index: 'user_id'})
                .eqJoin('project_id', r.table('projects'))
                .zip();
        }

        rql = addComputed(rql);

        return run(rql);
    }

    // addComputed adds additional attributes to the rql that
    // that are computed from other tables.
    function addComputed(rql) {
        rql = rql.merge(function(project) {
            return {
                users: r.table('access')
                    .getAll(project('id'), {index: 'project_id'})
                    .map(function(access) {
                        return access.merge({
                            'user': access('user_id')
                        });
                    })
                    .pluck('user', 'permissions')
                    .coerceTo('array'),
                reviews: r.table('reviews')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                notes: r.table('notes')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array'),
                events: r.table('events')
                    .getAll(project('id'), {index: 'project_id'})
                    .coerceTo('array')
            };
        });

        return rql;
    }
};
<|endoftext|>


<commit_before>'use strict';

//Native Nodejs modules
import fs from 'fs';
import path from 'path';

//Vendor modules
import fileExists from 'file-exists';
import _ from 'lodash';
import normalizePath from 'normalize-path';

//Source modules
import folderExists from './folder-exists';

/*
write both files and folder

should probably default to housing the files
and folders generated to a default named folder
to make it less more convenient to remove
accidentally created files or folders

read from the commandline or rc file
rc file has the name dirgen.config.js

*/

const createStructure = (linesInfo, rootPath) => {

  // console.log("createStructure", createStructure);
  // console.log("linesInfo is ", linesInfo);
  console.log("rootPath", rootPath);

  //Join the path safely
  let structureName = linesInfo.structureName,
    structureRoughPath = path.join(rootPath, structureName),
    structureCreatePath = normalizePath(structureRoughPath);

  console.log("structureCreatePath", structureCreatePath);

  if (linesInfo.inferType === 'file') {
    console.log("normalize root with struct path", structureCreatePath);
  } else {
    //Folder will be the only other structure type

  }

  /*
  How to keep track reliably of the current path?
  Keeping track of the plain string value will do
  by slicing and appending structure name to the string
  However, must ensure there must be certainty that
  the string manipulation will not cause
  race conditions
  */

  // if file
  //   createFile()
  // if folder
  //   createFolder(children, rootPath, parentDir)

  //Also start off creating siblings structure types
};

export default (linesInfo, rootPath) => {
  // console.log(`generation lines info is`, linesInfo);

  //TODO: Not taking in the rootpath directly
  //for now the path for now with dummy folder
  let hardCodeRootFolder = './testing';

  //Check for any specified folder to house
  //the files and folders to be generated

  //Hard code this folder for now
  if (folderExists('testing')) {
    console.log("folder exists and removing");
    fs.rmdirSync(hardCodeRootFolder);
    fs.mkdirSync(hardCodeRootFolder);
  } else {
    //Create when no root folder exists
    fs.mkdirSync(hardCodeRootFolder);
  }

  //Get the first line from the linesInfo
  createStructure(linesInfo.firstLine, hardCodeRootFolder);

};<commit_msg>Preparing for reading recursively<commit_after>'use strict';

//Native Nodejs modules
import fs from 'fs';
import path from 'path';

//Vendor modules
import fileExists from 'file-exists';
import _ from 'lodash';
import normalizePath from 'normalize-path';

//Source modules
import folderExists from './folder-exists';

/*
write both files and folder

should probably default to housing the files
and folders generated to a default named folder
to make it less more convenient to remove
accidentally created files or folders

read from the commandline or rc file
rc file has the name dirgen.config.js

*/

const createStructure = (linesInfo, rootPath) => {

  // console.log("createStructure", createStructure);
  // console.log("linesInfo is ", linesInfo);
  // console.log("rootPath", rootPath);

  //Join the path safely by converting all backward
  //slashes to forward slashes
  let structureName = linesInfo.structureName,
    structureRoughPath = path.join(rootPath, structureName),
    structureCreatePath = normalizePath(structureRoughPath);

  // console.log("structureCreatePath", structureCreatePath);

  if (linesInfo.inferType === 'file') {
    // console.log("normalize root with struct path", structureCreatePath);
    //Create self structure, file
    //callback is not really needed
    console.log("file", linesInfo.structureName);
  } else {
    //Folder will be the only other structure type
    //Create self structure, folder
    //callback produces the child structures
    console.log("folder", linesInfo.structureName);
  }

  if (!_.isUndefined(linesInfo.sibling) && linesInfo.sibling.length > 0) {

    console.log("some siblings");

  }

  //siblings need to be created too if present for files and folders
  //

  /*
  How to keep track reliably of the current path?
  Keeping track of the plain string value will do
  by slicing and appending structure name to the string
  However, must ensure there must be certainty that
  the string manipulation will not cause
  race conditions
  */

  // if file
  //   createFile()
  // if folder
  //   createFolder(children, rootPath, parentDir)

  //Also start off creating siblings structure types
};

export default (linesInfo, rootPath) => {
  // console.log(`generation lines info is`, linesInfo);

  //TODO: Not taking in the rootpath directly
  //for now the path for now with dummy folder
  let hardCodeRootFolder = './testing';

  //Check for any specified folder to house
  //the files and folders to be generated

  //Hard code this folder for now
  if (folderExists('testing')) {
    console.log("folder exists and removing");
    fs.rmdirSync(hardCodeRootFolder);
    fs.mkdirSync(hardCodeRootFolder);
  } else {
    //Create when no root folder exists
    fs.mkdirSync(hardCodeRootFolder);
  }

  //Get the first line from the linesInfo
  createStructure(linesInfo.firstLine, hardCodeRootFolder);

  //Also create the structure for all the siblings too

};<|endoftext|>


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

goog.provide('gf.Runtime');

goog.require('gf');
goog.require('gf.assets.BuildClient');
goog.require('gf.timing.Clock');
goog.require('goog.Disposable');
goog.require('goog.array');
goog.require('goog.asserts');

/**
 * Core runtime instance.
 *
 * @constructor
 * @extends {goog.Disposable}
 * @param {!gf.LaunchOptions} launchOptions Game options.
 * @param {gf.timing.Clock=} opt_clock Clock to use for time values.
 */
gf.Runtime = function(launchOptions, opt_clock) {
  goog.base(this);

  /**
   * Game options.
   * @type {!gf.LaunchOptions}
   */
  this.launchOptions = launchOptions;

  /**
   * Synchronized clock.
   * @type {!gf.timing.Clock}
   */
  this.clock = opt_clock || new gf.timing.Clock();

  /**
   * All registered components.
   * @type {!Array.<!gf.Component>}
   */
  this.components = [];

  /**
   * Build daemon client.
   * This will only be initialized if running with {@see gf.BUILD_CLIENT} set to
   * true and {@see gf.LaunchOptions#buildServer} is defined.
   * @type {gf.assets.BuildClient}
   */
  this.buildClient = null;
  if (gf.BUILD_CLIENT && launchOptions.buildServer) {
    this.buildClient = new gf.assets.BuildClient(this);
    this.registerDisposable(this.buildClient);
  }
};
goog.inherits(gf.Runtime, goog.Disposable);

/**
 * @override
 */
gf.Runtime.prototype.disposeInternal = function() {
  // Dispose all components
  goog.array.forEach(this.components, goog.dispose);
  this.components.length = 0;

  goog.base(this, 'disposeInternal');
};

/**
 * Adds a new component to the runtime.
 * @param {!gf.Component} component Component to add.
 */
gf.Runtime.prototype.addComponent = function(component) {
  goog.asserts.assert(!goog.array.contains(this.components, component));
  this.components.push(component);
};

/**
 * Removes an existing component from the runtime.
 * @param {!gf.Component} component Component to remove.
 */
gf.Runtime.prototype.removeComponent = function(component) {
  goog.array.remove(this.components, component);
};

/**
 * Gets the current game time.
 * @return {number} Game time, in seconds.
 */
gf.Runtime.prototype.getTime = function() {
  return this.clock.getServerTime();
};
<commit_msg>Removing some warnings in node builds<commit_after>/**
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

goog.provide('gf.Runtime');

goog.require('gf');
/** @suppress {extraRequire} */
goog.require('gf.Component');
goog.require('gf.assets.BuildClient');
goog.require('gf.timing.Clock');
goog.require('goog.Disposable');
goog.require('goog.array');
goog.require('goog.asserts');

/**
 * Core runtime instance.
 *
 * @constructor
 * @extends {goog.Disposable}
 * @param {!gf.LaunchOptions} launchOptions Game options.
 * @param {gf.timing.Clock=} opt_clock Clock to use for time values.
 */
gf.Runtime = function(launchOptions, opt_clock) {
  goog.base(this);

  /**
   * Game options.
   * @type {!gf.LaunchOptions}
   */
  this.launchOptions = launchOptions;

  /**
   * Synchronized clock.
   * @type {!gf.timing.Clock}
   */
  this.clock = opt_clock || new gf.timing.Clock();

  /**
   * All registered components.
   * @type {!Array.<!gf.Component>}
   */
  this.components = [];

  /**
   * Build daemon client.
   * This will only be initialized if running with {@see gf.BUILD_CLIENT} set to
   * true and {@see gf.LaunchOptions#buildServer} is defined.
   * @type {gf.assets.BuildClient}
   */
  this.buildClient = null;
  if (gf.BUILD_CLIENT && launchOptions.buildServer) {
    this.buildClient = new gf.assets.BuildClient(this);
    this.registerDisposable(this.buildClient);
  }
};
goog.inherits(gf.Runtime, goog.Disposable);

/**
 * @override
 */
gf.Runtime.prototype.disposeInternal = function() {
  // Dispose all components
  goog.array.forEach(this.components, goog.dispose);
  this.components.length = 0;

  goog.base(this, 'disposeInternal');
};

/**
 * Adds a new component to the runtime.
 * @param {!gf.Component} component Component to add.
 */
gf.Runtime.prototype.addComponent = function(component) {
  goog.asserts.assert(!goog.array.contains(this.components, component));
  this.components.push(component);
};

/**
 * Removes an existing component from the runtime.
 * @param {!gf.Component} component Component to remove.
 */
gf.Runtime.prototype.removeComponent = function(component) {
  goog.array.remove(this.components, component);
};

/**
 * Gets the current game time.
 * @return {number} Game time, in seconds.
 */
gf.Runtime.prototype.getTime = function() {
  return this.clock.getServerTime();
};
<|endoftext|>


<commit_before>import * as t from 'io-ts';
import pick from 'lodash/pick';
import assign from 'lodash/assign';
import findKey from 'lodash/findKey';
import sortBy from 'lodash/sortBy';

export function flatGraph(graph) {
  return Object.keys(graph).reduce((g, k) => ({
    ...g,
    [k]: graph[k],
    ...(graph[k].childNodes || {})
  }), {});
}

export function findP(graph, fetch) {
  return findKey(graph, { fetch });
}

function derivateA(graph, P) {
  const node = graph[P];
  return node.A;
}

// given a flat `AA` object, e.g.
//
//   { a: 3, foo: 'asd', bar: true }
//
// and an (possibly nested) `A` description, e.g.
//
//   [['a'], ['foo', 'bar']]
//
// returns an (possibly nested array containing) object(s) with the values
// corresponding to the given A keys, e.g.
//
//   [{ a: 3 }, { foo: 'asd', bar: true }]
//
function pickA(AA, A) {
  if (t.array(t.string).is(A)) {  // ['foo', 'bar']
    return pick(AA, A);
  } else {                        // [['foo', 'bar'], ['foo']]
    return A.map(a => pickA(AA, a));
  }
}

// given an array of `Ps`, aka "query ids"
// and an object of arguments `A`
// returns the same information rearranged to match the
// lower level `apply` signature expectations
export function queriesAndArgs(graph, Ps, A) {
  const queries = Ps.reduce((qs, P) => assign(qs, {
    [P]: graph[P].cachedFetch || graph[P].fetch // non-atoms are not cached, we return the naked fetch
  }), {});
  const args = Ps.reduce((argz, P) => assign(argz, {
    [P]: pickA(A, derivateA(graph, P))
  }), {});
  return { queries, args };
}

// e.g. if `C` depends on `B` depends on `A`
// given ['A', 'B', 'C'] in any order, returns ['C', 'B', 'A'] (dependants first, root last)
export function topoSorted(graph, Ps) {
  return sortBy(Ps, P => -graph[P].depth);
}
<commit_msg>remove useless derivateA<commit_after>import * as t from 'io-ts';
import pick from 'lodash/pick';
import assign from 'lodash/assign';
import findKey from 'lodash/findKey';
import sortBy from 'lodash/sortBy';

export function flatGraph(graph) {
  return Object.keys(graph).reduce((g, k) => ({
    ...g,
    [k]: graph[k],
    ...(graph[k].childNodes || {})
  }), {});
}

export function findP(graph, fetch) {
  return findKey(graph, { fetch });
}

// given a flat `AA` object, e.g.
//
//   { a: 3, foo: 'asd', bar: true }
//
// and an (possibly nested) `A` description, e.g.
//
//   [['a'], ['foo', 'bar']]
//
// returns an (possibly nested array containing) object(s) with the values
// corresponding to the given A keys, e.g.
//
//   [{ a: 3 }, { foo: 'asd', bar: true }]
//
function pickA(AA, A) {
  if (t.array(t.string).is(A)) {  // ['foo', 'bar']
    return pick(AA, A);
  } else {                        // [['foo', 'bar'], ['foo']]
    return A.map(a => pickA(AA, a));
  }
}

// given an array of `Ps`, aka "query ids"
// and an object of arguments `A`
// returns the same information rearranged to match the
// lower level `apply` signature expectations
export function queriesAndArgs(graph, Ps, A) {
  const queries = Ps.reduce((qs, P) => assign(qs, {
    [P]: graph[P].cachedFetch || graph[P].fetch // non-atoms are not cached, we return the naked fetch
  }), {});
  const args = Ps.reduce((argz, P) => assign(argz, {
    [P]: pickA(A, graph[P].A)
  }), {});
  return { queries, args };
}

// e.g. if `C` depends on `B` depends on `A`
// given ['A', 'B', 'C'] in any order, returns ['C', 'B', 'A'] (dependants first, root last)
export function topoSorted(graph, Ps) {
  return sortBy(Ps, P => -graph[P].depth);
}
<|endoftext|>


<commit_before>const path = require('path');
const fsPlus = require('ezzy-fs');
const handlebars = require('handlebars');
const HBS_REG = /\.hbs$/;
const logger = require('ezzy-logger').logger;

handlebars.registerPartial('styles', `
  {{#each assets.css}}
    <link rel="stylesheet" href="{{{.}}}" />
  {{/each}}
  {{#each styles}}
    <style type="text/css">
    {{{.}}}
    </style>
  {{/each}}
`);

handlebars.registerPartial('scripts', `
  {{#with bootstrap}}
    <script type="application/javascript">
    {{{.}}}
    </script>
  {{/with}}
  {{#each assets.js}}
    <script src="{{{.}}}"></script>
  {{/each}}
  {{#each scripts}}
    <script type="application/javascript">
    {{{.}}}
    </script>
  {{/each}}
`);

module.exports = dir => {

  const partials = path.normalize(dir + '/partials');

  return fsPlus.readdirPromise(partials)
    .then(files => {

      files = files.filter(file => HBS_REG.test(file));

      const fileNames =
        files.map(file => path.basename(file).replace(HBS_REG, ''));
      const promises =
        files.map(file => fsPlus
          .readFilePromise(path.normalize(dir + '/partials/' + file)));

      return Promise.all(promises)
        .then(values => {

          values.forEach((value, i) => {
            handlebars.registerPartial(fileNames[i], value.toString());
          });

          return handlebars;

        });

    }, () => {
      logger.warn(`No partials found ${partials}`);
      return handlebars;
    });
};
<commit_msg>Adding a section to include external assets separate from the dynamically computed external assets<commit_after>const path = require('path');
const fsPlus = require('ezzy-fs');
const handlebars = require('handlebars');
const HBS_REG = /\.hbs$/;
const logger = require('ezzy-logger').logger;

handlebars.registerPartial('styles', `
  {{#each externalStyles}}
    <link rel="stylesheet" href="{{{.}}}" />
  {{/each}}
  {{#each assets.css}}
    <link rel="stylesheet" href="{{{.}}}" />
  {{/each}}
  {{#each styles}}
    <style type="text/css">
    {{{.}}}
    </style>
  {{/each}}
`);

handlebars.registerPartial('scripts', `
  {{#with bootstrap}}
    <script type="application/javascript">
    {{{.}}}
    </script>
  {{/with}}
  {{#each externalScripts}}
    <script src="{{{.}}}"></script>
  {{/each}}
  {{#each assets.js}}
    <script src="{{{.}}}"></script>
  {{/each}}
  {{#each scripts}}
    <script type="application/javascript">
    {{{.}}}
    </script>
  {{/each}}
`);

module.exports = dir => {

  const partials = path.normalize(dir + '/partials');

  return fsPlus.readdirPromise(partials)
    .then(files => {

      files = files.filter(file => HBS_REG.test(file));

      const fileNames =
        files.map(file => path.basename(file).replace(HBS_REG, ''));
      const promises =
        files.map(file => fsPlus
          .readFilePromise(path.normalize(dir + '/partials/' + file)));

      return Promise.all(promises)
        .then(values => {

          values.forEach((value, i) => {
            handlebars.registerPartial(fileNames[i], value.toString());
          });

          return handlebars;

        });

    }, () => {
      logger.warn(`No partials found ${partials}`);
      return handlebars;
    });
};
<|endoftext|>


<commit_before>define(
	[
		'jquery'
	],

	function (jQuery) {

		/**
		 * @typedef {function} renderer
		 * @param {htmlCanvas} html
		 */

		/** @typedef {({}|string|renderer|widget|htmlTagBrush|*)} renderable */

		// Supported HTML 'tags'
		var tags = ('a abbr acronym address area article aside audio b bdi bdo big ' +
			'blockquote body br button canvas caption cite code col colgroup command ' +
			'datalist dd del details dfn div dl dt em embed fieldset figcaption figure ' +
			'footer form frame frameset h1 h2 h3 h4 h5 h6 hr head header hgroup html i ' +
			'iframe img input ins kbd keygen label legend li link map mark meta meter ' +
			'nav noscript object ol optgroup option output p param pre progress q rp rt' +
			'ruby samp script section select small source span strong style sub summary' +
			'sup table tbody td textarea tfoot th thead time title tr track tt ul var' +
			'video wbr').split(' ');

		// Supported HTML events
		var attributes = 'href for id media rel src style title type'.split(' ');

		// Supported HTML attributes
		var events = ('blur focus focusin focusout load resize scroll unload ' +
			'click dblclick mousedown mouseup mousemove mouseover ' +
			'mouseout mouseenter mouseleave change select submit ' +
			'keydown keypress keyup error dragstart dragenter dragover dragleave drop dragend').split(' ');

		/**
		 * htmlCanvas provides a DSL that we use to add elements to the DOM using a HTML looking syntax.
		 *
		 * The basic metaphor used is one of painting on a canvas using brushes. The canvas is the
		 * DOM and the brushes HTML 'tags'. Note that it have nothing to do with the HTML5 canvas tag
		 *
		 * @example
		 *		// A htmlCanvas is created on a jQuery object:
		 *		var html = htmlCanvas($('BODY'));
		 *
		 *		// We write HTML using standard tags:
		 *		html.h1('Hello World!');
		 *
		 *		// and standard attributes:
		 *		html.a({ id: 'id', href: 'http://www.google.se'}, 'Google');
		 *
		 *		// Callbacks can be attached to events:
		 *		html.a({click: function() { alert('Hello World!')} 'Click me!');
		 *
		 *		// Tags can be nested:
		 *		html.div({'class' : 'outer_div'},
		 *			html.div({'class' : 'inner_div'},
		 *				html.span('Some text')
		 *			)
		 *		);
		 *
		 *		// Parts can be assigned to variables:
		 *		var homeButton = html.a('Home').href('/');
		 *		if(showAlert) {
		 *			homeButton.click(function() { alert('Hello'); });
		 *		}
		 *
		 *
		 * @param {string|jQuery|htmlTagBrush} [rootElement] Element to  "paint" on. If not supplied a document fragment will be created
		 *
		 * @returns {htmlCanvas}
		 */
		function htmlCanvas(rootElement) {

			var root = htmlTagBrush({ element: rootElement });

			/** @typedef {{}} htmlCanvas */
			var that = {};

			//
			// Public
			//

			/**
			 * The root object that brushes will append elements to.
			 *
			 * @type {htmlTagBrush}
			 */
			that.root = root;

			/**
			 * Creates a brush that "paints" a tag of type tagName. Any children supplied
			 * will be appended as children to brush.
			 *
			 * @param {string} tagName Type of element (supported by document.createElement)
			 * @param {renderable[]} [children] Renderable objects to append as children of brush.
			 */
			that.tag = function (tagName, children) {
				var tagBrush = htmlTagBrush({ tag: tagName, children: children });
				root.appendBrush(tagBrush);
				return tagBrush;
			};

			/**
			 * Tags builders for each supported tag type.
			 *
			 * @example
			 *	html.h1('Title');
			 *	html.strong('Important stuff');
			 *	html.span(html.strong(userName), ' signed in.')
			 */
			tags.forEach(function (tagName) {
				that[tagName] = function () {
					var args = Array.prototype.slice.call(arguments);
					return that.tag(tagName, args);
				};
			});

			/**
			 * Append an object to the root brush
			 *
			 * @param anObject
			 */
			that.render = function (anObject) {
				root.render(anObject);
			};

			/**
			 * Append an unescaped HTML string to the root brush
			 */
			that.raw = function(htmlString) {
				root.html(htmlString);
			};

			return that;
		}

		/**
		 * A tag brush object represents a DOM element, built on a canvas. The element can
		 * be created from a 'tag' or an element matched using 'jQuery'.
		 *
		 * Note: A brush is usually only created from `htmlCanvas` and it should only
		 * be used once.
		 *
		 * @param {{}} spec
		 * @param {string} [spec.tag] Name of tag to create (using document.createElement)
		 * @param {string|jQuery|widget|htmlTagBrush|*} [spec.element]
		 * @param {renderable[]} [spec.children]
		 *
		 * @returns {htmlTagBrush}
		 */
		function htmlTagBrush(spec) {

			/** @typedef {{}} htmlTagBrush */
			var that = {};

			/**
			 * Create a new element from tagName or get it from elements.
			 *
			 * @type {HTMLElement}
			 */
			var element = spec.tag ? createElement(spec.tag) : getElement(spec.element);
			if (!element) {
				throw new Error('htmlTagBrush requires an element');
			}

			//
			// Public
			//

			/**
			 * DOM element created by brush
			 *
			 * @returns {HTMLElement}
			 */
			that.element = function () {
				return element;
			};

			/**
			 * Appends child objects to brush. Can take a single or several arguments.
			 *
			 * @example
			 *	html.h1().render(
			 *		'hello',
			 *		html.span('world',
			 *			function(html) {
			 *				html.img().src('foo.img');
			 *				}
			 *			)
			 *		);
			 *
			 * @param {renderable[]} arguments Any renderable objects
			 * @returns {htmlTagBrush}
			 */
			that.render = function () {
				var args = Array.prototype.slice.call(arguments);
				for (var i = 0; i < args.length; i++) {
					append(args[i]);
				}
				return that;
			};

			/**
			 * Implementation for `appendToBrush()` to allow a brush to be
			 * appended to another brush.
			 *
			 * @param {htmlTagBrush} aTagBrush
			 */
			that.appendToBrush = function (aTagBrush) {
				aTagBrush.appendBrush(that);
			};

			/**
			 * Append brush as child.
			 *
			 * @param {htmlTagBrush} aTagBrush
			 */
			that.appendBrush = appendBrush;

			/**
			 * Set unescaped html contents.
			 *
			 * @param {string} htmlContents
			 */
			that.html = function (htmlContents) {
				that.asJQuery().html(htmlContents);
			};

			/**
			 * Bind callback to DOM event
			 *
			 * @usage
			 *		html.a('click me').on('click', function() {
			 *			alert('click');
			 *		});
			 *
			 * @param {string} eventType One or more DOM event types, such as "click" or "submit," or custom event names.
			 * @param {function} callback A function to execute each time the event is triggered.
			 * @returns {{}}
			 */
			that.on = function (eventType, callback) {
				that.asJQuery().bind(eventType, callback);
				return that;
			};

			/**
			 * Event functions for each supported event type.
			 *
			 * @example
			 *	aBrush.click(function() { .. });
			 *	aBrush.blur(function() { .. });
			 */
			events.forEach(function(eventType) {
				that[eventType] = function (callback) {
					return that.on(eventType, callback);
				};
			});

			/**
			 * Adds a new attribute or changes the value of an existing attribute on the specified element.
			 * @param key
			 * @param value
			 * @returns {{}}
			 */
			that.setAttribute = function (key, value) {
				element.setAttribute(key, value);
				return that;
			};

			/**
			 * Accessors for each supported attribute.
			 *
			 * @example
			 *	aBrush.id('id');
			 *	aBrush.src('javascript:0');
			 *	aBrush.href('#');
			 */
			attributes.forEach(function(attributeName) {
				that[attributeName] = function (value) {
					return that.setAttribute(attributeName, value);
				};
			});

			/**
			 * Set element style with key/value or object literal.
			 *
			 * @example
			 *		html.h1().css('display', 'block');
			 *		html.h1().css({'display' : 'block', 'color' : 'red'});
			 *
			 * @param {string|{}} key
			 * @param {string} value
			 * @returns {{}}
			 */
			that.css = function (key, value) {
				if (typeof key === "string") {
					that.asJQuery().css(key, value);
				}
				else {
					that.asJQuery().css(key); // otherwise assume key is a map (object literal)
				}

				return that;
			};

			/**
			 * Set attributes using object literal.
			 *
			 * @example
			 *	html.h1().attr({id : 'myid', 'class' : 'myclass'});
			 *
			 * @note
			 *	Use klass or 'class' with quotation marks as key instead of class since its a reserved word.
			 *
			 * @param object
			 * @returns {{}}
			 */
			that.attr = function(object) {
				for (var key in object) {
					if (object.hasOwnProperty(key)) {
						// Attach functions
						if(typeof object[key] === "function") {
							that.on(key, object[key]);
						}

						if (key === 'klass') {
							that.addClass(object[key]);
						} else {
							that.setAttribute(key, object[key]);
						}
					}
				}
				return that;
			};

			/**
			 * Appends className to class attribute
			 * @param className
			 * @returns {htmlTagBrush}
			 */
			that.addClass = function (className) {
				that.asJQuery().addClass(className);
				return that;
			};

			/**
			 * Removes className from class attribute
			 *
			 * @param {string} className
			 * @returns {htmlTagBrush}
			 */
			that.removeClass = function (className) {
				that.asJQuery().removeClass(className);
				return that;
			};

			/**
			 * Returns jQuery that match element.
			 * @returns {jQuery}
			 */
			that.asJQuery = function () {
				return jQuery(that.element());
			};

			//
			// Private
			//

			/**
			 * Creates a new element from tagName
			 *
			 * @param {string} tagName
			 * @returns {Element}
			 */
			function createElement(tagName) {
				return document.createElement(tagName);
			}

			/**
			 * Appends object as child to brush. A tag brush knows how to append:
			 *
			 * - strings
			 * - functions (that take a htmlCanvas as argument)
			 * - other brushes and widgets (that implements `appendToBrush()`)
			 * - map / object literal with attributes (eg. {id: 'aId', 'class' : 'aClass'})
			 * - array of valid objects (see above)
			 *
			 * all other objects are appended using:
			 * `jQuery(element).append(object);`
			 *
			 * @param {renderable|renderable[]|{}} object
			 */
			function append(object) {
				if (typeof(object) === 'undefined' || object === null) {
					throw new Error('cannot append null or undefined to brush');
				}

				if (typeof object === "object" && object.constructor === Array) {
					for (var i = 0; i < object.length; i++) {
						append(object[i]);
					}
				}
				else if (typeof object === "string") {
					appendString(object);
				} else if (typeof object === "function") {
					appendFunction(object);
				} else if (typeof object === "object" &&
					object.appendToBrush /* eg. widget and tagBrush implement appendToBrush */) {
					object.appendToBrush(that); // double dispatch
				}
				else if (typeof object === "object") {
					that.attr(object); // assume attributes if none of above
				} else {
					jQuery(element).append(object); // default to jquery
				}
			}

			/**
			 * Appends DOM node as last child of element or concatenate with
			 * text if element can't have children.
			 *
			 * @param {string|HTMLElement} child
			 */
			function appendChild(child) {
				if (element.canHaveChildren !== false) {
					element.appendChild(child);
				} else {
					element.text = element.text + child.innerHTML;
				}
			}

			/**
			 * Appends element of brush
			 *
			 * @param {htmlTagBrush} aTagBrush
			 */
			function appendBrush(aTagBrush) {
				appendChild(aTagBrush.element());
			}

			/**
			 * Append text as child. `string` is escaped
			 *
			 * @param {string} string
			 */
			function appendString(string) {
				jQuery(element).append(document.createTextNode(string));
			}

			/**
			 * Append function by executing function with this element as canvas.
			 *
			 * @param {renderer} fn
			 */
			function appendFunction(fn) {
				var brushCanvas = htmlCanvas(that);
				fn(brushCanvas);
			}

			/**
			 * Element is set to first match if a jQuery was given.
			 *
			 * @param {string|jQuery|HTMLElement|widget|htmlTagBrush} [object]
			 * @returns {HTMLElement}
			 */
			function getElement(object) {

				// Create a fragment if no object
				if (typeof(object) === 'undefined' || object === null) {
					return  jQuery(document.createDocumentFragment()).get(0);
				}

				// Any object that implements asJQuery eg. widget and tagBrush
				if(typeof object === "object" && object.asJQuery) {
					return object.asJQuery().get(0);
				}

				// Fall back on jQuery if a string containing a selector expression,
				// a DOM Element, an existing jQuery object or any other argument that
				// jQuery accept (http://api.jquery.com/jQuery/)
				return jQuery(object).get(0);
			}

			//
			// Init
			//

			/**
			 * Append children to support nesting
			 *
			 * @example
			 *		html.ul(html.li(html.a({href: '#'}, 'home'));
			 */
			if(spec.children) {
				append(spec.children);
			}

			return that;
		}

		return htmlCanvas;
	}
);
<commit_msg>Return the receiver in tagBrush.html()<commit_after>define(
	[
		'jquery'
	],

	function (jQuery) {

		/**
		 * @typedef {function} renderer
		 * @param {htmlCanvas} html
		 */

		/** @typedef {({}|string|renderer|widget|htmlTagBrush|*)} renderable */

		// Supported HTML 'tags'
		var tags = ('a abbr acronym address area article aside audio b bdi bdo big ' +
			'blockquote body br button canvas caption cite code col colgroup command ' +
			'datalist dd del details dfn div dl dt em embed fieldset figcaption figure ' +
			'footer form frame frameset h1 h2 h3 h4 h5 h6 hr head header hgroup html i ' +
			'iframe img input ins kbd keygen label legend li link map mark meta meter ' +
			'nav noscript object ol optgroup option output p param pre progress q rp rt' +
			'ruby samp script section select small source span strong style sub summary' +
			'sup table tbody td textarea tfoot th thead time title tr track tt ul var' +
			'video wbr').split(' ');

		// Supported HTML events
		var attributes = 'href for id media rel src style title type'.split(' ');

		// Supported HTML attributes
		var events = ('blur focus focusin focusout load resize scroll unload ' +
			'click dblclick mousedown mouseup mousemove mouseover ' +
			'mouseout mouseenter mouseleave change select submit ' +
			'keydown keypress keyup error dragstart dragenter dragover dragleave drop dragend').split(' ');

		/**
		 * htmlCanvas provides a DSL that we use to add elements to the DOM using a HTML looking syntax.
		 *
		 * The basic metaphor used is one of painting on a canvas using brushes. The canvas is the
		 * DOM and the brushes HTML 'tags'. Note that it have nothing to do with the HTML5 canvas tag
		 *
		 * @example
		 *		// A htmlCanvas is created on a jQuery object:
		 *		var html = htmlCanvas($('BODY'));
		 *
		 *		// We write HTML using standard tags:
		 *		html.h1('Hello World!');
		 *
		 *		// and standard attributes:
		 *		html.a({ id: 'id', href: 'http://www.google.se'}, 'Google');
		 *
		 *		// Callbacks can be attached to events:
		 *		html.a({click: function() { alert('Hello World!')} 'Click me!');
		 *
		 *		// Tags can be nested:
		 *		html.div({'class' : 'outer_div'},
		 *			html.div({'class' : 'inner_div'},
		 *				html.span('Some text')
		 *			)
		 *		);
		 *
		 *		// Parts can be assigned to variables:
		 *		var homeButton = html.a('Home').href('/');
		 *		if(showAlert) {
		 *			homeButton.click(function() { alert('Hello'); });
		 *		}
		 *
		 *
		 * @param {string|jQuery|htmlTagBrush} [rootElement] Element to  "paint" on. If not supplied a document fragment will be created
		 *
		 * @returns {htmlCanvas}
		 */
		function htmlCanvas(rootElement) {

			var root = htmlTagBrush({ element: rootElement });

			/** @typedef {{}} htmlCanvas */
			var that = {};

			//
			// Public
			//

			/**
			 * The root object that brushes will append elements to.
			 *
			 * @type {htmlTagBrush}
			 */
			that.root = root;

			/**
			 * Creates a brush that "paints" a tag of type tagName. Any children supplied
			 * will be appended as children to brush.
			 *
			 * @param {string} tagName Type of element (supported by document.createElement)
			 * @param {renderable[]} [children] Renderable objects to append as children of brush.
			 */
			that.tag = function (tagName, children) {
				var tagBrush = htmlTagBrush({ tag: tagName, children: children });
				root.appendBrush(tagBrush);
				return tagBrush;
			};

			/**
			 * Tags builders for each supported tag type.
			 *
			 * @example
			 *	html.h1('Title');
			 *	html.strong('Important stuff');
			 *	html.span(html.strong(userName), ' signed in.')
			 */
			tags.forEach(function (tagName) {
				that[tagName] = function () {
					var args = Array.prototype.slice.call(arguments);
					return that.tag(tagName, args);
				};
			});

			/**
			 * Append an object to the root brush
			 *
			 * @param anObject
			 */
			that.render = function (anObject) {
				root.render(anObject);
			};

			/**
			 * Append an unescaped HTML string to the root brush
			 */
			that.raw = function(htmlString) {
				root.html(htmlString);
			};

			return that;
		}

		/**
		 * A tag brush object represents a DOM element, built on a canvas. The element can
		 * be created from a 'tag' or an element matched using 'jQuery'.
		 *
		 * Note: A brush is usually only created from `htmlCanvas` and it should only
		 * be used once.
		 *
		 * @param {{}} spec
		 * @param {string} [spec.tag] Name of tag to create (using document.createElement)
		 * @param {string|jQuery|widget|htmlTagBrush|*} [spec.element]
		 * @param {renderable[]} [spec.children]
		 *
		 * @returns {htmlTagBrush}
		 */
		function htmlTagBrush(spec) {

			/** @typedef {{}} htmlTagBrush */
			var that = {};

			/**
			 * Create a new element from tagName or get it from elements.
			 *
			 * @type {HTMLElement}
			 */
			var element = spec.tag ? createElement(spec.tag) : getElement(spec.element);
			if (!element) {
				throw new Error('htmlTagBrush requires an element');
			}

			//
			// Public
			//

			/**
			 * DOM element created by brush
			 *
			 * @returns {HTMLElement}
			 */
			that.element = function () {
				return element;
			};

			/**
			 * Appends child objects to brush. Can take a single or several arguments.
			 *
			 * @example
			 *	html.h1().render(
			 *		'hello',
			 *		html.span('world',
			 *			function(html) {
			 *				html.img().src('foo.img');
			 *				}
			 *			)
			 *		);
			 *
			 * @param {renderable[]} arguments Any renderable objects
			 * @returns {htmlTagBrush}
			 */
			that.render = function () {
				var args = Array.prototype.slice.call(arguments);
				for (var i = 0; i < args.length; i++) {
					append(args[i]);
				}
				return that;
			};

			/**
			 * Implementation for `appendToBrush()` to allow a brush to be
			 * appended to another brush.
			 *
			 * @param {htmlTagBrush} aTagBrush
			 */
			that.appendToBrush = function (aTagBrush) {
				aTagBrush.appendBrush(that);
			};

			/**
			 * Append brush as child.
			 *
			 * @param {htmlTagBrush} aTagBrush
			 */
			that.appendBrush = appendBrush;

			/**
			 * Set unescaped html contents.
			 *
			 * @param {string} htmlContents
			 */
			that.html = function (htmlContents) {
				that.asJQuery().html(htmlContents);
				return that;
			};

			/**
			 * Bind callback to DOM event
			 *
			 * @usage
			 *		html.a('click me').on('click', function() {
			 *			alert('click');
			 *		});
			 *
			 * @param {string} eventType One or more DOM event types, such as "click" or "submit," or custom event names.
			 * @param {function} callback A function to execute each time the event is triggered.
			 * @returns {{}}
			 */
			that.on = function (eventType, callback) {
				that.asJQuery().bind(eventType, callback);
				return that;
			};

			/**
			 * Event functions for each supported event type.
			 *
			 * @example
			 *	aBrush.click(function() { .. });
			 *	aBrush.blur(function() { .. });
			 */
			events.forEach(function(eventType) {
				that[eventType] = function (callback) {
					return that.on(eventType, callback);
				};
			});

			/**
			 * Adds a new attribute or changes the value of an existing attribute on the specified element.
			 * @param key
			 * @param value
			 * @returns {{}}
			 */
			that.setAttribute = function (key, value) {
				element.setAttribute(key, value);
				return that;
			};

			/**
			 * Accessors for each supported attribute.
			 *
			 * @example
			 *	aBrush.id('id');
			 *	aBrush.src('javascript:0');
			 *	aBrush.href('#');
			 */
			attributes.forEach(function(attributeName) {
				that[attributeName] = function (value) {
					return that.setAttribute(attributeName, value);
				};
			});

			/**
			 * Set element style with key/value or object literal.
			 *
			 * @example
			 *		html.h1().css('display', 'block');
			 *		html.h1().css({'display' : 'block', 'color' : 'red'});
			 *
			 * @param {string|{}} key
			 * @param {string} value
			 * @returns {{}}
			 */
			that.css = function (key, value) {
				if (typeof key === "string") {
					that.asJQuery().css(key, value);
				}
				else {
					that.asJQuery().css(key); // otherwise assume key is a map (object literal)
				}

				return that;
			};

			/**
			 * Set attributes using object literal.
			 *
			 * @example
			 *	html.h1().attr({id : 'myid', 'class' : 'myclass'});
			 *
			 * @note
			 *	Use klass or 'class' with quotation marks as key instead of class since its a reserved word.
			 *
			 * @param object
			 * @returns {{}}
			 */
			that.attr = function(object) {
				for (var key in object) {
					if (object.hasOwnProperty(key)) {
						// Attach functions
						if(typeof object[key] === "function") {
							that.on(key, object[key]);
						}

						if (key === 'klass') {
							that.addClass(object[key]);
						} else {
							that.setAttribute(key, object[key]);
						}
					}
				}
				return that;
			};

			/**
			 * Appends className to class attribute
			 * @param className
			 * @returns {htmlTagBrush}
			 */
			that.addClass = function (className) {
				that.asJQuery().addClass(className);
				return that;
			};

			/**
			 * Removes className from class attribute
			 *
			 * @param {string} className
			 * @returns {htmlTagBrush}
			 */
			that.removeClass = function (className) {
				that.asJQuery().removeClass(className);
				return that;
			};

			/**
			 * Returns jQuery that match element.
			 * @returns {jQuery}
			 */
			that.asJQuery = function () {
				return jQuery(that.element());
			};

			//
			// Private
			//

			/**
			 * Creates a new element from tagName
			 *
			 * @param {string} tagName
			 * @returns {Element}
			 */
			function createElement(tagName) {
				return document.createElement(tagName);
			}

			/**
			 * Appends object as child to brush. A tag brush knows how to append:
			 *
			 * - strings
			 * - functions (that take a htmlCanvas as argument)
			 * - other brushes and widgets (that implements `appendToBrush()`)
			 * - map / object literal with attributes (eg. {id: 'aId', 'class' : 'aClass'})
			 * - array of valid objects (see above)
			 *
			 * all other objects are appended using:
			 * `jQuery(element).append(object);`
			 *
			 * @param {renderable|renderable[]|{}} object
			 */
			function append(object) {
				if (typeof(object) === 'undefined' || object === null) {
					throw new Error('cannot append null or undefined to brush');
				}

				if (typeof object === "object" && object.constructor === Array) {
					for (var i = 0; i < object.length; i++) {
						append(object[i]);
					}
				}
				else if (typeof object === "string") {
					appendString(object);
				} else if (typeof object === "function") {
					appendFunction(object);
				} else if (typeof object === "object" &&
					object.appendToBrush /* eg. widget and tagBrush implement appendToBrush */) {
					object.appendToBrush(that); // double dispatch
				}
				else if (typeof object === "object") {
					that.attr(object); // assume attributes if none of above
				} else {
					jQuery(element).append(object); // default to jquery
				}
			}

			/**
			 * Appends DOM node as last child of element or concatenate with
			 * text if element can't have children.
			 *
			 * @param {string|HTMLElement} child
			 */
			function appendChild(child) {
				if (element.canHaveChildren !== false) {
					element.appendChild(child);
				} else {
					element.text = element.text + child.innerHTML;
				}
			}

			/**
			 * Appends element of brush
			 *
			 * @param {htmlTagBrush} aTagBrush
			 */
			function appendBrush(aTagBrush) {
				appendChild(aTagBrush.element());
			}

			/**
			 * Append text as child. `string` is escaped
			 *
			 * @param {string} string
			 */
			function appendString(string) {
				jQuery(element).append(document.createTextNode(string));
			}

			/**
			 * Append function by executing function with this element as canvas.
			 *
			 * @param {renderer} fn
			 */
			function appendFunction(fn) {
				var brushCanvas = htmlCanvas(that);
				fn(brushCanvas);
			}

			/**
			 * Element is set to first match if a jQuery was given.
			 *
			 * @param {string|jQuery|HTMLElement|widget|htmlTagBrush} [object]
			 * @returns {HTMLElement}
			 */
			function getElement(object) {

				// Create a fragment if no object
				if (typeof(object) === 'undefined' || object === null) {
					return  jQuery(document.createDocumentFragment()).get(0);
				}

				// Any object that implements asJQuery eg. widget and tagBrush
				if(typeof object === "object" && object.asJQuery) {
					return object.asJQuery().get(0);
				}

				// Fall back on jQuery if a string containing a selector expression,
				// a DOM Element, an existing jQuery object or any other argument that
				// jQuery accept (http://api.jquery.com/jQuery/)
				return jQuery(object).get(0);
			}

			//
			// Init
			//

			/**
			 * Append children to support nesting
			 *
			 * @example
			 *		html.ul(html.li(html.a({href: '#'}, 'home'));
			 */
			if(spec.children) {
				append(spec.children);
			}

			return that;
		}

		return htmlCanvas;
	}
);
<|endoftext|>


<commit_before>/* jslint node:true  */
/* global Promise: true, indexedDB: true */
'use strict';

var Schema = require('./schema/schema');
var QueryWrapper = require('./querywrapper');
var TransactionWrapper = require('./transactionwrapper');

/**
 * Constructor for IDB-wrapper
 * @param {String} database  The database to open
 * @param {Integer} version  The database version
 */
var IDBWrapper = function IDBWrapper(database, version) {
  this._databaseName = database;
  this._version = version;
  this._availableStores = [];
  Object.defineProperty(this, 'schema', {
    get: function () {
      if (!this._schema) {
        this._schema = new Schema(this);
      }
      return this._schema;
    }
  });

  this.open();
};

/**
 * Opens  or closes and re-opens a database connection
 * @param  {Boolean} closeAndReopen Should the current connection be closed and re-opened?
 * @return {Object}                 A promise
 */
IDBWrapper.prototype.open = function (closeAndReopen) {
  if (this._connectionPromise && !closeAndReopen) {
    return this._connectionPromise;
  }
  if (this._database) {
    this._database.close();
  }

  this._connectionPromise = new Promise(function (resolve, reject) {
    var initialReq = indexedDB.open(this._databaseName, this._version);

    initialReq.onsuccess = function (e) {
      this._database = e.target.result;
      this._defineStoreAccessors(this._database);
      resolve(this);
    }.bind(this);

    initialReq.onerror = function (e) {
      reject(new Error(e.target.error.message));
    };
    initialReq.onupgradeneeded = this._onUpgradeNeeded.bind(this);
  }.bind(this));

  return this._connectionPromise;
};

/**
 * Performs a transaction
 * @param  {Function} callback A callback performing the actions
 * @return {Object}            A promise
 */
IDBWrapper.prototype.transaction = function (stores,callback) {
  var transactionStores;
  var options = {};

  if (typeof stores !== 'function') {
    if (!Array.isArray(stores)) {
      throw new Error('Expecting an array of stores for the transaction');
    }
    transactionStores = stores.filter(function (store) {
      return this._availableStores.indexOf(store) !== -1;
    }, this);

    if (!transactionStores.length) {
      throw new Error('No valid stores specified');
    }
  } else {
    // no stores have been specified, so we should auto detect them
    options.autoDetectUsedStores = true;
    transactionStores = this._availableStores;
    // only a callback was passed in, reassign variable
    callback = stores;
  }

  var wrapper = new TransactionWrapper(transactionStores, callback, this._database, options);
  return wrapper.performTransaction();
};

/**
 * Defines store accessors on the IDB instance
 * @param  {IDBDatabase} db The database object
 */
IDBWrapper.prototype._defineStoreAccessors = function (db) {
  var existingStores = db.objectStoreNames;
  for (var i = 0, ii = existingStores.length; i< ii; i++) {
    var store = existingStores[i];
    this._availableStores.push(store);
    // these accessors will be only available AFTER connecting
    Object.defineProperty(this, store, {
      enumerable: true,
      configurable: true,
      get: (function (store) {
        return function () {
          return new QueryWrapper(store, this._connectionPromise);
        };
      }(store))
    });
  }
};

/**
 * Handles onupgradeneeded event
 * @param  {Object} e The event
 */
IDBWrapper.prototype._onUpgradeNeeded = function (e) {
  var db = e.target.result;
  var transaction = e.target.transaction;
  this.schema.runDDLStatements(db, transaction, e.oldVersion, e.newVersion);
  // TODO: clear schema ddl statements here?
};

/**
 * Adds data to the table/collection/store
 * @param  {String} collection The collection name
 * @param  {Array} values      An array of values to insert
 * @return {Object}            A promise
 */
IDBWrapper.prototype.insert = function (collection, values) {
  var q = new QueryWrapper(collection, this._connectionPromise);
  return q.insert(values);
};

/**
 * Either inserts or updates the passed in values
 * @param  {String} collection The collection name
 * @param  {Array} values      And array of values to upsert
 * @return {Object}            A promise
 */
IDBWrapper.prototype.upsert = function (collection, values) {
  var q = new QueryWrapper(collection, this._connectionPromise);
  return q.upsert(values);
};

/**
 * Find a single value from a collection by key
 * @param  {String} collection The collection name
 * @param  {Mixed} key         The key value
 * @param  {Boolean} required  Whether the promise will be rejected for an empty result
 * @return {Promise}           A promise
 */
IDBWrapper.prototype.findByKey = function (collection, key, required) {
  var q = new QueryWrapper(collection, this._connectionPromise);
  return q.find(key, required);
};

/**
 * Convenience method to construct a new instance
 * @param  {String} database  The database to open
 * @param  {Integer} version  The database version
 * @return {Object}           A new instance
 */
IDBWrapper.initialize = function (database, version) {
  return new IDBWrapper(database, version);
};

module.exports = IDBWrapper;
<commit_msg>Added basic export/import functionality<commit_after>/* jslint node:true  */
/* global Promise: true, indexedDB: true */
'use strict';

var Schema = require('./schema/schema');
var QueryWrapper = require('./querywrapper');
var TransactionWrapper = require('./transactionwrapper');

/**
 * Constructor for IDB-wrapper
 * @param {String} database  The database to open
 * @param {Integer} version  The database version
 */
var IDBWrapper = function IDBWrapper(database, version) {
  this._databaseName = database;
  this._version = version;
  this._availableStores = [];
  Object.defineProperty(this, 'schema', {
    get: function () {
      if (!this._schema) {
        this._schema = new Schema(this);
      }
      return this._schema;
    }
  });

  this.open();
};

/**
 * Opens  or closes and re-opens a database connection
 * @param  {Boolean} closeAndReopen Should the current connection be closed and re-opened?
 * @return {Object}                 A promise
 */
IDBWrapper.prototype.open = function (closeAndReopen) {
  if (this._connectionPromise && !closeAndReopen) {
    return this._connectionPromise;
  }
  if (this._database) {
    this._database.close();
  }

  this._connectionPromise = new Promise(function (resolve, reject) {
    var initialReq = indexedDB.open(this._databaseName, this._version);

    initialReq.onsuccess = function (e) {
      this._database = e.target.result;
      this._defineStoreAccessors(this._database);
      resolve(this);
    }.bind(this);

    initialReq.onerror = function (e) {
      reject(new Error(e.target.error.message));
    };
    initialReq.onupgradeneeded = this._onUpgradeNeeded.bind(this);
  }.bind(this));

  return this._connectionPromise;
};

/**
 * Performs a transaction
 * @param  {Function} callback A callback performing the actions
 * @return {Object}            A promise
 */
IDBWrapper.prototype.transaction = function (stores,callback) {
  var transactionStores;
  var options = {};

  if (typeof stores !== 'function') {
    if (!Array.isArray(stores)) {
      throw new Error('Expecting an array of stores for the transaction');
    }
    transactionStores = stores.filter(function (store) {
      return this._availableStores.indexOf(store) !== -1;
    }, this);

    if (!transactionStores.length) {
      throw new Error('No valid stores specified');
    }
  } else {
    // no stores have been specified, so we should auto detect them
    options.autoDetectUsedStores = true;
    transactionStores = this._availableStores;
    // only a callback was passed in, reassign variable
    callback = stores;
  }

  var wrapper = new TransactionWrapper(transactionStores, callback, this._database, options);
  return wrapper.performTransaction();
};

/**
 * Defines store accessors on the IDB instance
 * @param  {IDBDatabase} db The database object
 */
IDBWrapper.prototype._defineStoreAccessors = function (db) {
  var existingStores = db.objectStoreNames;
  for (var i = 0, ii = existingStores.length; i< ii; i++) {
    var store = existingStores[i];
    this._availableStores.push(store);
    // these accessors will be only available AFTER connecting
    Object.defineProperty(this, store, {
      enumerable: true,
      configurable: true,
      get: (function (store) {
        return function () {
          return new QueryWrapper(store, this._connectionPromise);
        };
      }(store))
    });
  }
};

/**
 * Handles onupgradeneeded event
 * @param  {Object} e The event
 */
IDBWrapper.prototype._onUpgradeNeeded = function (e) {
  var db = e.target.result;
  var transaction = e.target.transaction;
  this.schema.runDDLStatements(db, transaction, e.oldVersion, e.newVersion);
  // TODO: clear schema ddl statements here?
};

/**
 * Adds data to the table/collection/store
 * @param  {String} collection The collection name
 * @param  {Array} values      An array of values to insert
 * @return {Object}            A promise
 */
IDBWrapper.prototype.insert = function (collection, values) {
  var q = new QueryWrapper(collection, this._connectionPromise);
  return q.insert(values);
};

/**
 * Either inserts or updates the passed in values
 * @param  {String} collection The collection name
 * @param  {Array} values      And array of values to upsert
 * @return {Object}            A promise
 */
IDBWrapper.prototype.upsert = function (collection, values) {
  var q = new QueryWrapper(collection, this._connectionPromise);
  return q.upsert(values);
};

/**
 * Find a single value from a collection by key
 * @param  {String} collection The collection name
 * @param  {Mixed} key         The key value
 * @param  {Boolean} required  Whether the promise will be rejected for an empty result
 * @return {Promise}           A promise
 */
IDBWrapper.prototype.findByKey = function (collection, key, required) {
  var q = new QueryWrapper(collection, this._connectionPromise);
  return q.find(key, required);
};

/**
 * Exports the selected stores as a nested javascript object
 * @param  {Array} stores The stores to export, optional
 * @return {Object}       A promise which resolves with a javascript object holding the exported data
 */
IDBWrapper.prototype.export = function (stores) {
  stores = stores || this._availableStores;
  var selects = stores.map(function (store) {
    return this[store].findAll();
  }.bind(this));

  return Promise
  .all(selects)
  .then(function (results) {
    return stores.reduce(function (finalResult, store, index) {
      finalResult[store] = results[index];
      return finalResult;
    }, {});
  });
};

/**
 * Imports data from a javascript object or json string
 * @param  {Mixed} data A javascript object or JSON string
 * @return {Object}     A promise
 */
IDBWrapper.prototype.import = function (data) {
  if (typeof data === 'string') {
    try {
      data = JSON.parse(data);
    } catch (e) {
      return Promise.reject(e);
    }
  }

  var stores = Object.keys(data);
  var upserts = stores.map(function (store) {
    return this[store].upsert(data[store]);
  }.bind(this));

  return Promise.all(upserts);
};

/**
 * Convenience method to construct a new instance
 * @param  {String} database  The database to open
 * @param  {Integer} version  The database version
 * @return {Object}           A new instance
 */
IDBWrapper.initialize = function (database, version) {
  return new IDBWrapper(database, version);
};

module.exports = IDBWrapper;
<|endoftext|>