<commit_before>/**
 * Checklist-model
 * AngularJS directive for list of checkboxes
 * https://github.com/vitalets/checklist-model
 * License: MIT http://opensource.org/licenses/MIT
 */

angular.module('checklist-model', [])
.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
  // contains
  function contains(arr, item, comparator) {
    if (angular.isArray(arr)) {
      for (var i = arr.length; i--;) {
        if (comparator(arr[i], item)) {
          return true;
        }
      }
    }
    return false;
  }

  // add
  function add(arr, item, comparator) {
    arr = angular.isArray(arr) ? arr : [];
      if(!contains(arr, item, comparator)) {
          arr.push(item);
      }
    return arr;
  }

  // remove
  function remove(arr, item, comparator) {
    if (angular.isArray(arr)) {
      for (var i = arr.length; i--;) {
        if (comparator(arr[i], item)) {
          arr.splice(i, 1);
          break;
        }
      }
    }
    return arr;
  }

  // http://stackoverflow.com/a/19228302/1458162
  function postLinkFn(scope, elem, attrs) {
     // exclude recursion, but still keep the model
    var checklistModel = attrs.checklistModel;
    attrs.$set("checklistModel", null);
    // compile with `ng-model` pointing to `checked`
    $compile(elem)(scope);
    attrs.$set("checklistModel", checklistModel);

    // getter for original model
    var checklistModelGetter = $parse(checklistModel);
    var checklistChange = $parse(attrs.checklistChange);
    var checklistBeforeChange = $parse(attrs.checklistBeforeChange);
    var ngModelGetter = $parse(attrs.ngModel);

    // value added to list
    var value = attrs.checklistValue ? $parse(attrs.checklistValue)(scope.$parent) : attrs.value;

    var comparator = angular.equals;

    if (attrs.hasOwnProperty('checklistComparator')){
      if (attrs.checklistComparator[0] == '.') {
        var comparatorExpression = attrs.checklistComparator.substring(1);
        comparator = function (a, b) {
          return a[comparatorExpression] === b[comparatorExpression];
        };

      } else {
        comparator = $parse(attrs.checklistComparator)(scope.$parent);
      }
    }

    // watch UI checked change
    scope.$watch(attrs.ngModel, function(newValue, oldValue) {
      if (newValue === oldValue) {
        return;
      }

      if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
        ngModelGetter.assign(scope, contains(checklistModelGetter(scope.$parent), value, comparator));
        return;
      }

      setValueInChecklistModel(value, newValue);

      if (checklistChange) {
        checklistChange(scope);
      }
    });

    function setValueInChecklistModel(value, checked) {
      var current = checklistModelGetter(scope.$parent);
      if (angular.isFunction(checklistModelGetter.assign)) {
        if (checked === true) {
          checklistModelGetter.assign(scope.$parent, add(current, value, comparator));
        } else {
          checklistModelGetter.assign(scope.$parent, remove(current, value, comparator));
        }
      }

    }

    // declare one function to be used for both $watch functions
    function setChecked(newArr, oldArr) {
      if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
        setValueInChecklistModel(value, ngModelGetter(scope));
        return;
      }
      ngModelGetter.assign(scope, contains(newArr, value, comparator));
    }

    // watch original model change
    // use the faster $watchCollection method if it's available
    if (angular.isFunction(scope.$parent.$watchCollection)) {
        scope.$parent.$watchCollection(checklistModel, setChecked);
    } else {
        scope.$parent.$watch(checklistModel, setChecked, true);
    }
  }

  return {
    restrict: 'A',
    priority: 1000,
    terminal: true,
    scope: true,
    compile: function(tElement, tAttrs) {
      if ((tElement[0].tagName !== 'INPUT' || tAttrs.type !== 'checkbox') && (tElement[0].tagName !== 'MD-CHECKBOX') && (!tAttrs.btnCheckbox)) {
        throw 'checklist-model should be applied to `input[type="checkbox"]` or `md-checkbox`.';
      }

      if (!tAttrs.checklistValue && !tAttrs.value) {
        throw 'You should provide `value` or `checklist-value`.';
      }

      // by default ngModel is 'checked', so we set it if not specified
      if (!tAttrs.ngModel) {
        // local scope var storing individual checkbox model
        tAttrs.$set("ngModel", "checked");
      }

      return postLinkFn;
    }
  };
}]);
<commit_msg>remove counterproductive restriction<commit_after>/**
 * Checklist-model
 * AngularJS directive for list of checkboxes
 * https://github.com/vitalets/checklist-model
 * License: MIT http://opensource.org/licenses/MIT
 */

angular.module('checklist-model', [])
.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
  // contains
  function contains(arr, item, comparator) {
    if (angular.isArray(arr)) {
      for (var i = arr.length; i--;) {
        if (comparator(arr[i], item)) {
          return true;
        }
      }
    }
    return false;
  }

  // add
  function add(arr, item, comparator) {
    arr = angular.isArray(arr) ? arr : [];
      if(!contains(arr, item, comparator)) {
          arr.push(item);
      }
    return arr;
  }

  // remove
  function remove(arr, item, comparator) {
    if (angular.isArray(arr)) {
      for (var i = arr.length; i--;) {
        if (comparator(arr[i], item)) {
          arr.splice(i, 1);
          break;
        }
      }
    }
    return arr;
  }

  // http://stackoverflow.com/a/19228302/1458162
  function postLinkFn(scope, elem, attrs) {
     // exclude recursion, but still keep the model
    var checklistModel = attrs.checklistModel;
    attrs.$set("checklistModel", null);
    // compile with `ng-model` pointing to `checked`
    $compile(elem)(scope);
    attrs.$set("checklistModel", checklistModel);

    // getter for original model
    var checklistModelGetter = $parse(checklistModel);
    var checklistChange = $parse(attrs.checklistChange);
    var checklistBeforeChange = $parse(attrs.checklistBeforeChange);
    var ngModelGetter = $parse(attrs.ngModel);

    // value added to list
    var value = attrs.checklistValue ? $parse(attrs.checklistValue)(scope.$parent) : attrs.value;

    var comparator = angular.equals;

    if (attrs.hasOwnProperty('checklistComparator')){
      if (attrs.checklistComparator[0] == '.') {
        var comparatorExpression = attrs.checklistComparator.substring(1);
        comparator = function (a, b) {
          return a[comparatorExpression] === b[comparatorExpression];
        };

      } else {
        comparator = $parse(attrs.checklistComparator)(scope.$parent);
      }
    }

    // watch UI checked change
    scope.$watch(attrs.ngModel, function(newValue, oldValue) {
      if (newValue === oldValue) {
        return;
      }

      if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
        ngModelGetter.assign(scope, contains(checklistModelGetter(scope.$parent), value, comparator));
        return;
      }

      setValueInChecklistModel(value, newValue);

      if (checklistChange) {
        checklistChange(scope);
      }
    });

    function setValueInChecklistModel(value, checked) {
      var current = checklistModelGetter(scope.$parent);
      if (angular.isFunction(checklistModelGetter.assign)) {
        if (checked === true) {
          checklistModelGetter.assign(scope.$parent, add(current, value, comparator));
        } else {
          checklistModelGetter.assign(scope.$parent, remove(current, value, comparator));
        }
      }

    }

    // declare one function to be used for both $watch functions
    function setChecked(newArr, oldArr) {
      if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
        setValueInChecklistModel(value, ngModelGetter(scope));
        return;
      }
      ngModelGetter.assign(scope, contains(newArr, value, comparator));
    }

    // watch original model change
    // use the faster $watchCollection method if it's available
    if (angular.isFunction(scope.$parent.$watchCollection)) {
        scope.$parent.$watchCollection(checklistModel, setChecked);
    } else {
        scope.$parent.$watch(checklistModel, setChecked, true);
    }
  }

  return {
    restrict: 'A',
    priority: 1000,
    terminal: true,
    scope: true,
    compile: function(tElement, tAttrs) {

      if (!tAttrs.checklistValue && !tAttrs.value) {
        throw 'You should provide `value` or `checklist-value`.';
      }

      // by default ngModel is 'checked', so we set it if not specified
      if (!tAttrs.ngModel) {
        // local scope var storing individual checkbox model
        tAttrs.$set("ngModel", "checked");
      }

      return postLinkFn;
    }
  };
}]);
<|endoftext|>


<commit_before>XOSTenantSite = XOSModel.extend( {
    listFields: ["name", "allocated"],
    modelName: "tenantSite",
    collectionName: "tenantSites"
});

XOSTenantSiteCollection = XOSCollection.extend( {
    listFields: ["name", "allocated"],
    modelName: "tenantSite",
    collectionName: "tenantSites",

    getFromSlice: function(slice) {
        var tenantSites = [];
        var id = 0;
        for (siteName in slice.attributes.site_allocation) {
            allocated = slice.attributes.site_allocation[siteName];
            tenantSites.push(new XOSTenantSite( { name: siteName, allocated: allocated, id: id} ));
            id = id + 1;
        }
        for (index in xos.tenantview.models[0].attributes.blessed_site_names) {
            siteName = xos.tenantview.models[0].attributes.blessed_site_names[index];
            if (! (siteName in slice.attributes.site_allocation)) {
                tenantSites.push(new XOSTenantSite( { name: siteName, allocated: 0, id: id} ));
                id = id + 1;
            }
        }
        this.set(tenantSites);
    },

    putToSlice: function(slice) {
        slice.attributes.site_allocation = {};
        for (index in this.models) {
            var model = this.models[index];
            slice.attributes.site_allocation[ model.attributes.name ] = model.attributes.allocated;
        }
    },
});

XOSEditUsersView = Marionette.ItemView.extend({
            template: "#tenant-edit-users",
            viewInitializers: [],

            onShow: function() {
                _.each(this.viewInitializers, function(initializer) {
                    initializer();
                });
            },

            templateHelpers: function() { return { detailView: this, model: this.model }; },

            });

XOSTenantButtonView = Marionette.ItemView.extend({
            template: "#xos-tenant-buttons-template",

            events: {"click button.btn-tenant-create": "createClicked",
                     "click button.btn-tenant-delete": "deleteClicked",
                     "click button.btn-tenant-add-user": "addUserClicked",
                     "click button.btn-tenant-save": "saveClicked",
                     },

            createClicked: function(e) {
                     XOSTenantApp.addSlice();
                     },

            deleteClicked: function(e) {
                     XOSTenantApp.deleteSlice(this.options.linkedView.model);
                     },

            addUserClicked: function(e) {
                     XOSTenantApp.editUsers(this.options.linkedView.model);
                     },

            saveClicked: function(e) {
                     model = this.options.linkedView.model;
                     model.tenantSiteCollection.putToSlice(model);
                     model.attributes.users = model.usersBuffer;
                     this.options.linkedView.submitContinueClicked.call(this.options.linkedView, e);
                     },
            });

XOSTenantApp = new XOSApplication({
    logTableId: "#logTable",
    statusMsgId: "#statusMsg",
    hideTabsByDefault: true,
    varName: "XOSTenantApp",
});

XOSTenantApp.addRegions({
    tenantSliceSelector: "#tenantSliceSelector",
    tenantSummary: "#tenantSummary",
    tenantSiteList: "#tenantSiteList",
    tenantButtons: "#tenantButtons",
    tenantAddSliceInterior: "#tenant-addslice-interior",
    tenantEditUsersInterior: "#tenant-edit-users-interior",
});

XOSTenantApp.buildViews = function() {
     XOSTenantApp.tenantSites = new XOSTenantSiteCollection();

     tenantSummaryClass = XOSDetailView.extend({template: "#xos-detail-template",
                                                app: XOSTenantApp,
                                                detailFields: ["serviceClass", "default_image", "default_flavor", "network_ports", "mount_data_sets"],
                                                fieldDisplayNames: {serviceClass: "Service Level", "default_flavor": "Flavor", "default_image": "Image", "mount_data_sets": "Data Sets"},

                                                onShow: function() {
                                                    // the slice selector is in a different table, so make every label cell the maximal width
                                                    make_same_width("#xos-tenant-view-panel", ".xos-label-cell");
                                                },
                                                });

     XOSTenantApp.tenantSummaryView = tenantSummaryClass;

     tenantAddClass = XOSDetailView.extend({template: "#xos-detail-template",
                                                app: XOSTenantApp,
                                                detailFields: ["name", "description"]});

     XOSTenantApp.tenantAddView = tenantAddClass;

     tenantSiteItemClass = XOSItemView.extend({template: "#xos-listitem-template",
                                               app: XOSTenantApp});

     XOSTenantApp.tenantSiteItemView = tenantSiteItemClass;

     tenantSiteListClass = XOSDataTableView.extend({template: "#xos-list-template",
                                               app: XOSTenantApp,
                                               childView: tenantSiteItemClass,
                                               collection: XOSTenantApp.tenantSites,
                                               title: "sites",
                                               inputType: {allocated: "spinner"},
                                               noDeleteColumn: true,
                                               disablePaginate: true,
                                               disableFilter: true,
                                               fieldDisplayNames: {"name": "Site"},
                                               });

     XOSTenantApp.tenantSiteListView = tenantSiteListClass;

     XOSTenantApp.tenantSliceSelectorView = SliceSelectorView.extend( {
         sliceChanged: function(id) {
             XOSTenantApp.navToSlice(id);
         },
     });

     xos.sites.fetch();
     xos.slicesPlus.fetch();
     xos.tenantview.fetch();
};

make_choices = function(list_of_names, list_of_values) {
    result = [];
    if (!list_of_values) {
        for (index in list_of_names) {
            displayName = list_of_names[index];
            result.push( [displayName, displayName] );
        }
    } else {
        for (index in list_of_names) {
            displayName = list_of_names[index];
            id = list_of_values[index];
            result.push( [displayName, id] );
        }
    }
    return result;
};

XOSTenantApp.navToSlice = function(id) {
    XOSTenantApp.viewSlice(xos.slicesPlus.get(id));
};

XOSTenantApp.adjustCollectionField = function(collectionName, id, fieldName, amount) {
    model = XOSTenantApp[collectionName].get(id);
    model.set(fieldName, Math.max(model.get(fieldName) + amount, 0));
};

XOSTenantApp.addSlice = function() {
    var app=this;

    if (!xos.tenant().current_user_can_create_slice) {
        window.alert("You do not have sufficient rights to create a slice on your site");
        return;
    }

    model = new xos.slicesPlus.model({site: xos.tenant().current_user_site_id,
                                      name: xos.tenant().current_user_login_base + "_",
                                      creator: xos.tenant().current_user_id});
    console.log(model);
    var detailView = new XOSTenantApp.tenantAddView({model: model,
                                                     collection: xos.slicesPlus,
                                                     noSubmitButton: true,
                                                    });
    detailView.dialog = $("#tenant-addslice-dialog");
    app.tenantAddSliceInterior.show(detailView);
    $("#tenant-addslice-dialog").dialog({
       autoOpen: false,
       modal: true,
       width: 640,
       buttons : {
            "Create Slice" : function() {
              var addDialog = this;
              console.log("SAVE!!!");
              detailView.synchronous = true;
              detailView.afterSave = function() { $(addDialog).dialog("close"); XOSTenantApp.navToSlice(detailView.model.id); }
              detailView.save();
            },
            "Cancel" : function() {
              $(this).dialog("close");
            }
          }
        });
    $("#tenant-addslice-dialog").dialog("open");
};

XOSTenantApp.editUsers = function(model) {
    var app=this;
    var detailView = new XOSEditUsersView({model: model, collection: xos.slicesPlus});
    detailView.dialog = $("#tenant-edit-users-dialog");
    app.tenantEditUsersInterior.show(detailView);
    $("#tenant-edit-users-dialog").dialog({
       autoOpen: false,
       modal: true,
       width: 640,
       buttons : {
            "Ok" : function() {
              var editDialog = this;
              user_ids = all_options($("#tenant-edit-users-dialog").find(".select-picker-to"));
              user_ids = user_ids.map( function(x) { return parseInt(x,10); } );
              model.usersBuffer = user_ids;
              $(editDialog).dialog("close");
            },
            "Cancel" : function() {
              $(this).dialog("close");
            }
          }
        });
    $("#tenant-edit-users-dialog").dialog("open");
};

XOSTenantApp.deleteSlice = function(model) {
    var app=this;
    app.deleteDialog(model, function() { console.log("afterDelete"); app.viewSlice(undefined); });
};

XOSTenantApp.viewSlice = function(model) {
    if (!model && xos.slicesPlus.models.length > 0) {
        model = xos.slicesPlus.models[0];
    }

    if (model) {
        sliceSelector = new XOSTenantApp.tenantSliceSelectorView({collection: xos.slicesPlus,
                                                                  selectedID: model ? model.id : null,
                                                                 } );
        XOSTenantApp.sliceSelector = sliceSelector;
        XOSTenantApp.tenantSliceSelector.show(sliceSelector);

        tenantSummary = new XOSTenantApp.tenantSummaryView({model: model,
                                                            choices: {mount_data_sets: make_choices(xos.tenant().public_volume_names, null),
                                                                      serviceClass: make_choices(xos.tenant().blessed_service_class_names, xos.tenant().blessed_service_classes),
                                                                      default_image: make_choices(xos.tenant().blessed_image_names, xos.tenant().blessed_images),
                                                                      default_flavor: make_choices(xos.tenant().blessed_flavor_names, xos.tenant().blessed_flavors),},
                                                           });
        XOSTenantApp.tenantSummary.show(tenantSummary);

        tenantSites = new XOSTenantSiteCollection();
        tenantSites.getFromSlice(model);
        model.usersBuffer = model.attributes.users; /* save a copy of 'users' that we can edit. This prevents another view (developer) from overwriting our copy with a fetch from the server */
        model.usersOrig = model.attributes.users;   /* save an immutable copy that we'll use for username lookups */
        model.user_namesOrig = model.attributes.user_names;
        model.tenantSiteCollection = tenantSites;
        XOSTenantApp.tenantSites = tenantSites;

        tenantSiteList = new XOSTenantApp.tenantSiteListView({collection: tenantSites });
        XOSTenantApp.tenantSiteList.show(tenantSiteList);
        // on xos.slicePlus.sort, need to update xostenantapp.tenantSites

        XOSTenantApp.tenantButtons.show( new XOSTenantButtonView( { app: XOSTenantApp,
                                                                    linkedView: tenantSummary } ) );
    } else {
        XOSTenantApp.tenantSliceSelector.show(new HTMLView({html: ""}));
        XOSTenantApp.tenantSummary.show(new HTMLView({html: "You have no slices"}));
        XOSTenantApp.tenantSiteList.show(new HTMLView({html: ""}));
        XOSTenantApp.tenantButtons.show( new XOSTenantButtonView( { template: "#xos-tenant-buttons-noslice-template",
                                                                    app: XOSTenantApp,
                                                                    linkedView: tenantSummary } ) );
    }
};

XOSTenantApp.sanityCheck = function() {
    errors = [];
    if (xos.tenant().blessed_service_classes.length == 0) {
        errors.push("no blessed service classes");
    }
    if (xos.tenant().blessed_flavors.length == 0) {
        errors.push("no blessed flavors");
    }
    if (xos.tenant().blessed_images.length == 0) {
        errors.push("no blessed images");
    }
    if (xos.tenant().blessed_sites.length == 0) {
        errors.push("no blessed sites");
    }
    if (xos.tenant().current_user_site_id == null) {
        errors.push("current user does not have a site");
    }

    if (errors.length > 0) {
         $("#tenantSummary").html("Tenant view sanity check failed
" + errors.join("
"));
         return false;
    }

    return true;
}

XOSTenantApp.collectionLoadChange = function() {
    stats = xos.getCollectionStatus();

    if (!XOSTenantApp.navigationStarted) {
        if (stats["isLoaded"] + stats["failedLoad"] >= stats["startedLoad"]) {
            if (XOSTenantApp.sanityCheck()) {
                XOSTenantApp.viewSlice(undefined);
            }
        } else {
            $("#tenantSummary").html("
Loading...

");
            $("#xos-startup-progress").progressbar({value: stats["completedLoad"], max: stats["startedLoad"]});
        }
    }
};

XOSTenantApp.on("start", function() {
     XOSTenantApp.buildViews();

     // fire it once to initially show the progress bar
     XOSTenantApp.collectionLoadChange();

     // fire it each time the collection load status is updated
     Backbone.on("xoslib:collectionLoadChange", XOSTenantApp.collectionLoadChange);
});

$(document).ready(function(){
    XOSTenantApp.start();
});

<commit_msg>make save button green when slice is dirty<commit_after>XOSTenantSite = XOSModel.extend( {
    listFields: ["name", "allocated"],
    modelName: "tenantSite",
    collectionName: "tenantSites"
});

XOSTenantSiteCollection = XOSCollection.extend( {
    listFields: ["name", "allocated"],
    modelName: "tenantSite",
    collectionName: "tenantSites",

    getFromSlice: function(slice) {
        var tenantSites = [];
        var id = 0;
        for (siteName in slice.attributes.site_allocation) {
            allocated = slice.attributes.site_allocation[siteName];
            tenantSites.push(new XOSTenantSite( { name: siteName, allocated: allocated, id: id} ));
            id = id + 1;
        }
        for (index in xos.tenantview.models[0].attributes.blessed_site_names) {
            siteName = xos.tenantview.models[0].attributes.blessed_site_names[index];
            if (! (siteName in slice.attributes.site_allocation)) {
                tenantSites.push(new XOSTenantSite( { name: siteName, allocated: 0, id: id} ));
                id = id + 1;
            }
        }
        this.set(tenantSites);
    },

    putToSlice: function(slice) {
        slice.attributes.site_allocation = {};
        for (index in this.models) {
            var model = this.models[index];
            slice.attributes.site_allocation[ model.attributes.name ] = model.attributes.allocated;
        }
    },
});

XOSEditUsersView = Marionette.ItemView.extend({
            template: "#tenant-edit-users",
            viewInitializers: [],

            onShow: function() {
                _.each(this.viewInitializers, function(initializer) {
                    initializer();
                });
            },

            templateHelpers: function() { return { detailView: this, model: this.model }; },

            });

XOSTenantSummaryView = XOSDetailView.extend({
            events: {"change": "onChange"},

            onChange: function(e) {
                XOSTenantApp.setDirty(true);
            },

            saveSuccess: function() {
                console.log("saveSuccess!");
                XOSTenantApp.setDirty(false);
            },

            });

XOSTenantButtonView = Marionette.ItemView.extend({
            template: "#xos-tenant-buttons-template",

            events: {"click button.btn-tenant-create": "createClicked",
                     "click button.btn-tenant-delete": "deleteClicked",
                     "click button.btn-tenant-add-user": "addUserClicked",
                     "click button.btn-tenant-save": "saveClicked",
                     },

            createClicked: function(e) {
                     XOSTenantApp.addSlice();
                     },

            deleteClicked: function(e) {
                     XOSTenantApp.deleteSlice(this.options.linkedView.model);
                     },

            addUserClicked: function(e) {
                     XOSTenantApp.editUsers(this.options.linkedView.model);
                     },

            saveClicked: function(e) {
                     model = this.options.linkedView.model;
                     model.tenantSiteCollection.putToSlice(model);
                     model.attributes.users = model.usersBuffer;

                     e.preventDefault();
                     this.options.linkedView.save();
                     //this.options.linkedView.submitContinueClicked.call(this.options.linkedView, e);
                     //XOSTenantApp.setDirty(false);
                     },
            });

XOSTenantApp = new XOSApplication({
    logTableId: "#logTable",
    statusMsgId: "#statusMsg",
    hideTabsByDefault: true,
    varName: "XOSTenantApp",
});

XOSTenantApp.addRegions({
    tenantSliceSelector: "#tenantSliceSelector",
    tenantSummary: "#tenantSummary",
    tenantSiteList: "#tenantSiteList",
    tenantButtons: "#tenantButtons",
    tenantAddSliceInterior: "#tenant-addslice-interior",
    tenantEditUsersInterior: "#tenant-edit-users-interior",
});

XOSTenantApp.setDirty = function(dirty) {
    if (dirty) {
        $("button.btn-tenant-save").addClass("btn-success");
    } else {
        $("button.btn-tenant-save").removeClass("btn-success");
    }
};

XOSTenantApp.buildViews = function() {
     XOSTenantApp.tenantSites = new XOSTenantSiteCollection();

     tenantSummaryClass = XOSTenantSummaryView.extend({template: "#xos-detail-template",
                                                app: XOSTenantApp,
                                                detailFields: ["serviceClass", "default_image", "default_flavor", "network_ports", "mount_data_sets"],
                                                fieldDisplayNames: {serviceClass: "Service Level", "default_flavor": "Flavor", "default_image": "Image", "mount_data_sets": "Data Sets"},

                                                onShow: function() {
                                                    // the slice selector is in a different table, so make every label cell the maximal width
                                                    make_same_width("#xos-tenant-view-panel", ".xos-label-cell");
                                                },
                                                });

     XOSTenantApp.tenantSummaryView = tenantSummaryClass;

     tenantAddClass = XOSDetailView.extend({template: "#xos-detail-template",
                                                app: XOSTenantApp,
                                                detailFields: ["name", "description"]});

     XOSTenantApp.tenantAddView = tenantAddClass;

     tenantSiteItemClass = XOSItemView.extend({template: "#xos-listitem-template",
                                               app: XOSTenantApp});

     XOSTenantApp.tenantSiteItemView = tenantSiteItemClass;

     tenantSiteListClass = XOSDataTableView.extend({template: "#xos-list-template",
                                               app: XOSTenantApp,
                                               childView: tenantSiteItemClass,
                                               collection: XOSTenantApp.tenantSites,
                                               title: "sites",
                                               inputType: {allocated: "spinner"},
                                               noDeleteColumn: true,
                                               disablePaginate: true,
                                               disableFilter: true,
                                               fieldDisplayNames: {"name": "Site"},
                                               });

     XOSTenantApp.tenantSiteListView = tenantSiteListClass;

     XOSTenantApp.tenantSliceSelectorView = SliceSelectorView.extend( {
         sliceChanged: function(id) {
             XOSTenantApp.navToSlice(id);
         },
     });

     xos.sites.fetch();
     xos.slicesPlus.fetch();
     xos.tenantview.fetch();
};

make_choices = function(list_of_names, list_of_values) {
    result = [];
    if (!list_of_values) {
        for (index in list_of_names) {
            displayName = list_of_names[index];
            result.push( [displayName, displayName] );
        }
    } else {
        for (index in list_of_names) {
            displayName = list_of_names[index];
            id = list_of_values[index];
            result.push( [displayName, id] );
        }
    }
    return result;
};

XOSTenantApp.navToSlice = function(id) {
    XOSTenantApp.viewSlice(xos.slicesPlus.get(id));
};

XOSTenantApp.adjustCollectionField = function(collectionName, id, fieldName, amount) {
    model = XOSTenantApp[collectionName].get(id);
    model.set(fieldName, Math.max(model.get(fieldName) + amount, 0));
    XOSTenantApp.setDirty(true);
};

XOSTenantApp.addSlice = function() {
    var app=this;

    if (!xos.tenant().current_user_can_create_slice) {
        window.alert("You do not have sufficient rights to create a slice on your site");
        return;
    }

    model = new xos.slicesPlus.model({site: xos.tenant().current_user_site_id,
                                      name: xos.tenant().current_user_login_base + "_",
                                      creator: xos.tenant().current_user_id});
    console.log(model);
    var detailView = new XOSTenantApp.tenantAddView({model: model,
                                                     collection: xos.slicesPlus,
                                                     noSubmitButton: true,
                                                    });
    detailView.dialog = $("#tenant-addslice-dialog");
    app.tenantAddSliceInterior.show(detailView);
    $("#tenant-addslice-dialog").dialog({
       autoOpen: false,
       modal: true,
       width: 640,
       buttons : {
            "Create Slice" : function() {
              var addDialog = this;
              console.log("SAVE!!!");
              detailView.synchronous = true;
              detailView.afterSave = function() { $(addDialog).dialog("close"); XOSTenantApp.navToSlice(detailView.model.id); }
              detailView.save();
            },
            "Cancel" : function() {
              $(this).dialog("close");
            }
          }
        });
    $("#tenant-addslice-dialog").dialog("open");
};

XOSTenantApp.editUsers = function(model) {
    var app=this;
    var detailView = new XOSEditUsersView({model: model, collection: xos.slicesPlus});
    detailView.dialog = $("#tenant-edit-users-dialog");
    app.tenantEditUsersInterior.show(detailView);
    $("#tenant-edit-users-dialog").dialog({
       autoOpen: false,
       modal: true,
       width: 640,
       buttons : {
            "Ok" : function() {
              var editDialog = this;
              user_ids = all_options($("#tenant-edit-users-dialog").find(".select-picker-to"));
              user_ids = user_ids.map( function(x) { return parseInt(x,10); } );
              model.usersBuffer = user_ids;
              $(editDialog).dialog("close");
            },
            "Cancel" : function() {
              $(this).dialog("close");
            }
          }
        });
    $("#tenant-edit-users-dialog").dialog("open");
};

XOSTenantApp.deleteSlice = function(model) {
    var app=this;
    app.deleteDialog(model, function() { console.log("afterDelete"); app.viewSlice(undefined); });
};

XOSTenantApp.viewSlice = function(model) {
    if (!model && xos.slicesPlus.models.length > 0) {
        model = xos.slicesPlus.models[0];
    }

    if (model) {
        sliceSelector = new XOSTenantApp.tenantSliceSelectorView({collection: xos.slicesPlus,
                                                                  selectedID: model ? model.id : null,
                                                                 } );
        XOSTenantApp.sliceSelector = sliceSelector;
        XOSTenantApp.tenantSliceSelector.show(sliceSelector);

        tenantSummary = new XOSTenantApp.tenantSummaryView({model: model,
                                                            choices: {mount_data_sets: make_choices(xos.tenant().public_volume_names, null),
                                                                      serviceClass: make_choices(xos.tenant().blessed_service_class_names, xos.tenant().blessed_service_classes),
                                                                      default_image: make_choices(xos.tenant().blessed_image_names, xos.tenant().blessed_images),
                                                                      default_flavor: make_choices(xos.tenant().blessed_flavor_names, xos.tenant().blessed_flavors),},
                                                           });
        XOSTenantApp.tenantSummary.show(tenantSummary);

        tenantSites = new XOSTenantSiteCollection();
        tenantSites.getFromSlice(model);
        model.usersBuffer = model.attributes.users; /* save a copy of 'users' that we can edit. This prevents another view (developer) from overwriting our copy with a fetch from the server */
        model.usersOrig = model.attributes.users;   /* save an immutable copy that we'll use for username lookups */
        model.user_namesOrig = model.attributes.user_names;
        model.tenantSiteCollection = tenantSites;
        XOSTenantApp.tenantSites = tenantSites;

        tenantSiteList = new XOSTenantApp.tenantSiteListView({collection: tenantSites });
        XOSTenantApp.tenantSiteList.show(tenantSiteList);
        // on xos.slicePlus.sort, need to update xostenantapp.tenantSites

        XOSTenantApp.tenantButtons.show( new XOSTenantButtonView( { app: XOSTenantApp,
                                                                    linkedView: tenantSummary } ) );
    } else {
        XOSTenantApp.tenantSliceSelector.show(new HTMLView({html: ""}));
        XOSTenantApp.tenantSummary.show(new HTMLView({html: "You have no slices"}));
        XOSTenantApp.tenantSiteList.show(new HTMLView({html: ""}));
        XOSTenantApp.tenantButtons.show( new XOSTenantButtonView( { template: "#xos-tenant-buttons-noslice-template",
                                                                    app: XOSTenantApp,
                                                                    linkedView: tenantSummary } ) );
    }
};

XOSTenantApp.sanityCheck = function() {
    errors = [];
    if (xos.tenant().blessed_service_classes.length == 0) {
        errors.push("no blessed service classes");
    }
    if (xos.tenant().blessed_flavors.length == 0) {
        errors.push("no blessed flavors");
    }
    if (xos.tenant().blessed_images.length == 0) {
        errors.push("no blessed images");
    }
    if (xos.tenant().blessed_sites.length == 0) {
        errors.push("no blessed sites");
    }
    if (xos.tenant().current_user_site_id == null) {
        errors.push("current user does not have a site");
    }

    if (errors.length > 0) {
         $("#tenantSummary").html("Tenant view sanity check failed
" + errors.join("
"));
         return false;
    }

    return true;
}

XOSTenantApp.collectionLoadChange = function() {
    stats = xos.getCollectionStatus();

    if (!XOSTenantApp.navigationStarted) {
        if (stats["isLoaded"] + stats["failedLoad"] >= stats["startedLoad"]) {
            if (XOSTenantApp.sanityCheck()) {
                XOSTenantApp.viewSlice(undefined);
            }
        } else {
            $("#tenantSummary").html("
Loading...

");
            $("#xos-startup-progress").progressbar({value: stats["completedLoad"], max: stats["startedLoad"]});
        }
    }
};

XOSTenantApp.on("start", function() {
     XOSTenantApp.buildViews();

     // fire it once to initially show the progress bar
     XOSTenantApp.collectionLoadChange();

     // fire it each time the collection load status is updated
     Backbone.on("xoslib:collectionLoadChange", XOSTenantApp.collectionLoadChange);
});

$(document).ready(function(){
    XOSTenantApp.start();
});

<|endoftext|>


<commit_before>!function(n){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.DragDrop=n()}}(function(){return function n(t,e,r){function o(a,u){if(!e[a]){if(!t[a]){var f="function"==typeof require&&require;if(!u&&f)return f(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var s=e[a]={exports:{}};t[a][0].call(s.exports,function(n){var e=t[a][1][n];return o(e?e:n)},s,s.exports,n,t,e,r)}return e[a].exports}for(var i="function"==typeof require&&require,a=0;a<r.length;a++)o(r[a]);return o}({1:[function(n,t,e){function r(){s=!1,u.length?c=u.concat(c):l=-1,c.length&&o()}function o(){if(!s){var n=setTimeout(r);s=!0;for(var t=c.length;t;){for(u=c,c=[];++l<t;)u[l].run();l=-1,t=c.length}u=null,s=!1,clearTimeout(n)}}function i(n,t){this.fun=n,this.array=t}function a(){}var u,f=t.exports={},c=[],s=!1,l=-1;f.nextTick=function(n){var t=new Array(arguments.length-1);if(arguments.length>1)for(var e=1;e<arguments.length;e++)t[e-1]=arguments[e];c.push(new i(n,t)),1!==c.length||s||setTimeout(o,0)},i.prototype.run=function(){this.fun.apply(null,this.array)},f.title="browser",f.browser=!0,f.env={},f.argv=[],f.version="",f.versions={},f.on=a,f.addListener=a,f.once=a,f.off=a,f.removeListener=a,f.removeAllListeners=a,f.emit=a,f.binding=function(n){throw new Error("process.binding is not supported")},f.cwd=function(){return"/"},f.chdir=function(n){throw new Error("process.chdir is not supported")},f.umask=function(){return 0}},{}],2:[function(n,t,e){t.exports=function(n,t){function e(n,r){return n.reduce(function(n,o){return Array.isArray(o)&&t>r?n.concat(e(o,r+1)):n.concat(o)},[])}return t="number"==typeof t?t:1/0,e(n,1)}},{}],3:[function(n,t,e){function r(n,t,e){var r=!0,f=!0;if("function"!=typeof n)throw new TypeError(a);return e===!1?r=!1:o(e)&&(r="leading"in e?!!e.leading:r,f="trailing"in e?!!e.trailing:f),u.leading=r,u.maxWait=+t,u.trailing=f,i(n,t,u)}function o(n){var t=typeof n;return!!n&&("object"==t||"function"==t)}var i=n("lodash.debounce"),a="Expected a function",u={leading:!1,maxWait:0,trailing:!1};t.exports=r},{"lodash.debounce":4}],4:[function(n,t,e){function r(n,t,e){function r(){m&&clearTimeout(m),p&&clearTimeout(p),p=m=y=void 0}function i(){var e=t-(c()-v);if(0>=e||e>t){p&&clearTimeout(p);var r=y;p=m=y=void 0,r&&(h=c(),d=n.apply(g,l),m||p||(l=g=null))}else m=setTimeout(i,e)}function f(){m&&clearTimeout(m),p=m=y=void 0,(x||w!==t)&&(h=c(),d=n.apply(g,l),m||p||(l=g=null))}function s(){if(l=arguments,v=c(),g=this,y=x&&(m||!T),w===!1)var e=T&&!m;else{p||T||(h=v);var r=w-(v-h),o=0>=r||r>w;o?(p&&(p=clearTimeout(p)),h=v,d=n.apply(g,l)):p||(p=setTimeout(f,r))}return o&&m?m=clearTimeout(m):m||t===w||(m=setTimeout(i,t)),e&&(o=!0,d=n.apply(g,l)),!o||m||p||(l=g=null),d}var l,p,d,v,g,m,y,h=0,w=!1,x=!0;if("function"!=typeof n)throw new TypeError(a);if(t=0>t?0:+t||0,e===!0){var T=!0;x=!1}else o(e)&&(T=e.leading,w="maxWait"in e&&u(+e.maxWait||0,t),x="trailing"in e?e.trailing:x);return s.cancel=r,s}function o(n){var t=typeof n;return!!n&&("object"==t||"function"==t)}var i=n("lodash._getnative"),a="Expected a function",u=Math.max,f=i(Date,"now"),c=f||function(){return(new Date).getTime()};t.exports=r},{"lodash._getnative":5}],5:[function(n,t,e){function r(n){return"string"==typeof n?n:null==n?"":n+""}function o(n){return!!n&&"object"==typeof n}function i(n,t){var e=null==n?void 0:n[t];return a(e)?e:void 0}function a(n){return null==n?!1:g.call(n)==f?m.test(d.call(n)):o(n)&&l.test(n)}function u(n){return n=r(n),n&&s.test(n)?n.replace(c,"\\$&"):n}var f="[object Function]",c=/[.*+?^${}()|[\]\/\\]/g,s=RegExp(c.source),l=/^\[object .+?Constructor\]$/,p=Object.prototype,d=Function.prototype.toString,v=p.hasOwnProperty,g=p.toString,m=RegExp("^"+u(d.call(v)).replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=i},{}],6:[function(n,t,e){var r=n("dezalgo");t.exports=function(n,t){function e(n,e,r){o[n]=r,(0===--i||e)&&(t&&t(e,o),t=null)}t&&(t=r(t));var o,i,a;Array.isArray(n)?(o=[],i=n.length):(a=Object.keys(n),o={},i=a.length),i?a?a.forEach(function(t){n[t](e.bind(void 0,t))}):n.forEach(function(n,t){n(e.bind(void 0,t))}):(t&&t(null,o),t=null)}},{dezalgo:7}],7:[function(n,t,e){function r(n){var t=!0;return i(function(){t=!1}),function(){var e=arguments,r=this;t?i(function(){n.apply(r,e)}):n.apply(r,e)}}var o=n("wrappy");t.exports=o(r);var i=n("asap")},{asap:8,wrappy:9}],8:[function(n,t,e){(function(n){function e(){for(;o.next;){o=o.next;var n=o.task;o.task=void 0;var t=o.domain;t&&(o.domain=void 0,t.enter());try{n()}catch(r){if(f)throw t&&t.exit(),setTimeout(e,0),t&&t.enter(),r;setTimeout(function(){throw r},0)}t&&t.exit()}a=!1}function r(t){i=i.next={task:t,domain:f&&n.domain,next:null},a||(a=!0,u())}var o={task:void 0,next:null},i=o,a=!1,u=void 0,f=!1;if("undefined"!=typeof n&&n.nextTick)f=!0,u=function(){n.nextTick(e)};else if("function"==typeof setImmediate)u="undefined"!=typeof window?setImmediate.bind(window,e):function(){setImmediate(e)};else if("undefined"!=typeof MessageChannel){var c=new MessageChannel;c.port1.onmessage=e,u=function(){c.port2.postMessage(0)}}else u=function(){setTimeout(e,0)};t.exports=r}).call(this,n("_process"))},{_process:1}],9:[function(n,t,e){function r(n,t){function e(){for(var t=new Array(arguments.length),e=0;e<t.length;e++)t[e]=arguments[e];var r=n.apply(this,t),o=t[t.length-1];return"function"==typeof r&&r!==o&&Object.keys(o).forEach(function(n){r[n]=o[n]}),r}if(n&&t)return r(n)(t);if("function"!=typeof n)throw new TypeError("need wrapper function");return Object.keys(n).forEach(function(t){e[t]=n[t]}),e}t.exports=r},{}],"/":[function(n,t,e){function r(n,t){"string"==typeof n&&(n=document.querySelector(n));var e=i(n),r=a(n,t);return n.addEventListener("dragenter",o,!1),n.addEventListener("dragover",e,!1),n.addEventListener("drop",r,!1),function(){n instanceof Element&&n.classList.remove("drag"),n.removeEventListener("dragenter",o,!1),n.removeEventListener("dragover",e,!1),n.removeEventListener("drop",r,!1)}}function o(n){return n.stopPropagation(),n.preventDefault(),!1}function i(n){var t=l(function(){n instanceof Element&&n.classList.add("drag"),n.timeout&&clearTimeout(n.timeout),n.timeout=setTimeout(function(){n instanceof Element&&n.classList.remove("drag")},150)},100,{trailing:!1});return function(n){n.stopPropagation(),n.preventDefault(),n.dataTransfer.dropEffect="copy",t()}}function a(n,t){return function(e){e.stopPropagation(),e.preventDefault(),n instanceof Element&&n.classList.remove("drag");var r={x:e.clientX,y:e.clientY};return e.dataTransfer.items?s(f(e.dataTransfer.items).map(function(n){return function(t){u(n.webkitGetAsEntry(),t)}}),function(n,e){n||t(c(e),r)}):t(f(e.dataTransfer.files),r),!1}}function u(n,t){function e(){i.readEntries(function(n){n.length>0?(o=o.concat(f(n)),e()):r()})}function r(){s(o.map(function(n){return function(t){u(n,t)}}),t)}var o=[];if(n.isFile)n.file(function(n){t(null,n)});else if(n.isDirectory){var i=n.createReader();e()}}function f(n){return Array.prototype.slice.call(n||[],0)}t.exports=r;var c=n("flatten"),s=n("run-parallel"),l=n("lodash.throttle")},{flatten:2,"lodash.throttle":3,"run-parallel":6}]},{},[])("/")});
<commit_msg>build<commit_after>!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n;n="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,n.DragDrop=t()}}(function(){return function t(n,e,r){function o(a,u){if(!e[a]){if(!n[a]){var f="function"==typeof require&&require;if(!u&&f)return f(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=e[a]={exports:{}};n[a][0].call(l.exports,function(t){var e=n[a][1][t];return o(e?e:t)},l,l.exports,t,n,e,r)}return e[a].exports}for(var i="function"==typeof require&&require,a=0;a<r.length;a++)o(r[a]);return o}({1:[function(t,n,e){n.exports=function(t,n){function e(t,r){return t.reduce(function(t,o){return Array.isArray(o)&&n>r?t.concat(e(o,r+1)):t.concat(o)},[])}return n="number"==typeof n?n:1/0,e(t,1)}},{}],2:[function(t,n,e){function r(t,n,e){var r=!0,u=!0;if("function"!=typeof t)throw new TypeError(a);return e===!1?r=!1:o(e)&&(r="leading"in e?!!e.leading:r,u="trailing"in e?!!e.trailing:u),i(t,n,{leading:r,maxWait:+n,trailing:u})}function o(t){var n=typeof t;return!!t&&("object"==n||"function"==n)}var i=t("lodash.debounce"),a="Expected a function";n.exports=r},{"lodash.debounce":3}],3:[function(t,n,e){function r(t,n,e){function r(){h&&clearTimeout(h),d&&clearTimeout(d),w=0,d=h=m=void 0}function i(n,e){e&&clearTimeout(e),d=h=m=void 0,n&&(w=c(),v=t.apply(y,p),h||d||(p=y=void 0))}function f(){var t=n-(c()-g);0>=t||t>n?i(m,d):h=setTimeout(f,t)}function l(){i(b,h)}function s(){if(p=arguments,g=c(),y=this,m=b&&(h||!x),E===!1)var e=x&&!h;else{d||x||(w=g);var r=E-(g-w),o=0>=r||r>E;o?(d&&(d=clearTimeout(d)),w=g,v=t.apply(y,p)):d||(d=setTimeout(l,r))}return o&&h?h=clearTimeout(h):h||n===E||(h=setTimeout(f,n)),e&&(o=!0,v=t.apply(y,p)),!o||h||d||(p=y=void 0),v}var p,d,v,g,y,h,m,w=0,E=!1,b=!0;if("function"!=typeof t)throw new TypeError(a);if(n=0>n?0:+n||0,e===!0){var x=!0;b=!1}else o(e)&&(x=!!e.leading,E="maxWait"in e&&u(+e.maxWait||0,n),b="trailing"in e?!!e.trailing:b);return s.cancel=r,s}function o(t){var n=typeof t;return!!t&&("object"==n||"function"==n)}var i=t("lodash._getnative"),a="Expected a function",u=Math.max,f=i(Date,"now"),c=f||function(){return(new Date).getTime()};n.exports=r},{"lodash._getnative":4}],4:[function(t,n,e){function r(t){return!!t&&"object"==typeof t}function o(t,n){var e=null==t?void 0:t[n];return u(e)?e:void 0}function i(t){return a(t)&&d.call(t)==f}function a(t){var n=typeof t;return!!t&&("object"==n||"function"==n)}function u(t){return null==t?!1:i(t)?v.test(s.call(t)):r(t)&&c.test(t)}var f="[object Function]",c=/^\[object .+?Constructor\]$/,l=Object.prototype,s=Function.prototype.toString,p=l.hasOwnProperty,d=l.toString,v=RegExp("^"+s.call(p).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");n.exports=o},{}],5:[function(t,n,e){var r=t("dezalgo");n.exports=function(t,n){function e(t,e,r){o[t]=r,(0===--i||e)&&(n&&n(e,o),n=null)}n&&(n=r(n));var o,i,a;Array.isArray(t)?(o=[],i=t.length):(a=Object.keys(t),o={},i=a.length),i?a?a.forEach(function(n){t[n](e.bind(void 0,n))}):t.forEach(function(t,n){t(e.bind(void 0,n))}):(n&&n(null,o),n=null)}},{dezalgo:6}],6:[function(t,n,e){function r(t){var n=!0;return i(function(){n=!1}),function(){var e=arguments,r=this;n?i(function(){t.apply(r,e)}):t.apply(r,e)}}var o=t("wrappy");n.exports=o(r);var i=t("asap")},{asap:7,wrappy:9}],7:[function(t,n,e){"use strict";function r(){if(f.length)throw f.shift()}function o(t){var n;n=u.length?u.pop():new i,n.task=t,a(n)}function i(){this.task=null}var a=t("./raw"),u=[],f=[],c=a.makeRequestCallFromTimer(r);n.exports=o,i.prototype.call=function(){try{this.task.call()}catch(t){o.onerror?o.onerror(t):(f.push(t),c())}finally{this.task=null,u[u.length]=this}}},{"./raw":8}],8:[function(t,n,e){(function(t){"use strict";function e(t){u.length||(a(),f=!0),u[u.length]=t}function r(){for(;c<u.length;){var t=c;if(c+=1,u[t].call(),c>l){for(var n=0,e=u.length-c;e>n;n++)u[n]=u[n+c];u.length-=c,c=0}}u.length=0,c=0,f=!1}function o(t){var n=1,e=new s(t),r=document.createTextNode("");return e.observe(r,{characterData:!0}),function(){n=-n,r.data=n}}function i(t){return function(){function n(){clearTimeout(e),clearInterval(r),t()}var e=setTimeout(n,0),r=setInterval(n,50)}}n.exports=e;var a,u=[],f=!1,c=0,l=1024,s=t.MutationObserver||t.WebKitMutationObserver;a="function"==typeof s?o(r):i(r),e.requestFlush=a,e.makeRequestCallFromTimer=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],9:[function(t,n,e){function r(t,n){function e(){for(var n=new Array(arguments.length),e=0;e<n.length;e++)n[e]=arguments[e];var r=t.apply(this,n),o=n[n.length-1];return"function"==typeof r&&r!==o&&Object.keys(o).forEach(function(t){r[t]=o[t]}),r}if(t&&n)return r(t)(n);if("function"!=typeof t)throw new TypeError("need wrapper function");return Object.keys(t).forEach(function(n){e[n]=t[n]}),e}n.exports=r},{}],"/":[function(t,n,e){function r(t,n){"string"==typeof t&&(t=document.querySelector(t));var e=i(t),r=a(t,n);return t.addEventListener("dragenter",o,!1),t.addEventListener("dragover",e,!1),t.addEventListener("drop",r,!1),function(){t instanceof Element&&t.classList.remove("drag"),t.removeEventListener("dragenter",o,!1),t.removeEventListener("dragover",e,!1),t.removeEventListener("drop",r,!1)}}function o(t){return t.stopPropagation(),t.preventDefault(),!1}function i(t){var n=s(function(){t instanceof Element&&t.classList.add("drag"),t.timeout&&clearTimeout(t.timeout),t.timeout=setTimeout(function(){t instanceof Element&&t.classList.remove("drag")},150)},100,{trailing:!1});return function(t){t.stopPropagation(),t.preventDefault(),t.dataTransfer.dropEffect="copy",n()}}function a(t,n){return function(e){e.stopPropagation(),e.preventDefault(),t instanceof Element&&t.classList.remove("drag");var r={x:e.clientX,y:e.clientY};if(e.dataTransfer.items)l(f(e.dataTransfer.items).map(function(t){return function(n){u(t.webkitGetAsEntry(),n)}}),function(t,e){t||n(c(e),r)});else{var o=f(e.dataTransfer.files);o.forEach(function(t){t.fullPath="/"+t.name}),n(o,r)}return!1}}function u(t,n){function e(){i.readEntries(function(t){t.length>0?(o=o.concat(f(t)),e()):r()})}function r(){l(o.map(function(t){return function(n){u(t,n)}}),n)}var o=[];if(t.isFile)t.file(function(e){e.fullPath=t.fullPath,n(null,e)});else if(t.isDirectory){var i=t.createReader();e()}}function f(t){return Array.prototype.slice.call(t||[],0)}n.exports=r;var c=t("flatten"),l=t("run-parallel"),s=t("lodash.throttle")},{flatten:1,"lodash.throttle":2,"run-parallel":5}]},{},[])("/")});
<|endoftext|>


<commit_before>/**
 * Handle Error.
 */
class LandsWarError extends Error {
	/**
	 * Define name of the error and set the message.
	 * @param {String} message - The message.
	 */
	constructor(message) {
		super(message);

		this.name = 'LandsWarError';
	}
}

/**
 * Handle error for every socket.
 * @param {Error} error - The error Object.
 * @param {Function} callback - The socket callback function.
 */
function handleSocketError(error, callback) {
	if ((error.name === undefined || error.name === 'ValidationError' || error.name === 'LandsWarError') && error.message) {
		logger.error(error.message);
		callback({ message: error.message });
	} else {
		logger.error(error);
		callback({ message: 'An unknown error happened' });
	}
}

module.exports = {
	LandsWarError,
	handleSocketError,
};
<commit_msg>errorSockets: Boom response for WebSockets errors<commit_after>const Boom = require('boom');

/**
 * Handle Error.
 */
class LandsWarError extends Error {
	/**
	 * Define name of the error and set the message.
	 * @param {String} message - The message.
	 */
	constructor(message) {
		super(message);

		this.name = 'LandsWarError';
	}
}

/**
 * Handle error for every socket.
 * @param {Error} error - The error Object.
 * @param {Function} callback - The socket callback function.
 */
function handleSocketError(error, callback) {
	if ((error.name === undefined || error.name === 'ValidationError' || error.name === 'LandsWarError') && error.message) {
		logger.error(error.message);
		callback(Boom.badRequest(error.message));
	} else {
		logger.error(error);
		callback(Boom.badImplementation());
	}
}

module.exports = {
	LandsWarError,
	handleSocketError,
};
<|endoftext|>


<commit_before>/* global jQuery */

(function ($) {
    'use strict';

    $(document).ready(function () {

        $('.toctree-l1')
            .has('.toctree-l2')
            .prepend('');

        // Toggle global nav on mobile
        $('.navbar-toggle').click(function () {
            var $this = $(this);

            if ($this.attr('aria-expanded') === 'false') {
                $this.attr('aria-expanded', true);
            } else {
                $this.attr('aria-expanded', false);
            }

            $('.nav-main-level').slideToggle(200);
        });

        // Toggle subnav on mobile
        $('.dropdown-toggle').click(function () {
            var $this = $(this);

            if ($this.attr('aria-expanded') === 'false') {
                $this.attr('aria-expanded', true);
            } else {
                $this.attr('aria-expanded', false);
            }

            $this.siblings('.nav-sub-level').slideToggle(200);
        });

        // open sub nav on mobile
        $('.doc-main-body').click(function () {
            if ($(window).width() < 992) {
                $('.doc-main-body').addClass('open');
            }
        });

        // close sub nav on mobile
        $('.doc-wrapper').click(function (e) {
            e.stopPropagation();

            if ($(window).width() < 992) {
                $('.doc-btn.open').click();
                $('.doc-main-body').removeClass('open');
            }
        });

        // open subnav for documentation nav
        $('.toctree-l1 > .glyphicon').click(function () {
            ToggleTree($(this));
        });

        // open subnav for current page
        $('.toctree-l1.current')
            .has('.toctree-l2')
            .children('.glyphicon')
            .click();

        // set the height of the documentation page when there isn't enough content to fill the page
        setDocumentHeight();

        // and check to do the same on resize
        $(window).smartresize(function () {
            setTimeout(function () { setDocumentHeight(); }, 270);
        });

        // adjust the HTML rendered by Sphinx for the TOC
        $('.article-nav-wrapper > ul')
            .attr({
                id: 'articleNavAffix',
                class: 'affix-top'
            })
            .children('li')
            .children()
            .wrapAll('

')
            .siblings('ul')
            .addClass('nav nav-tabs article-nav');

        //affix the article nav to the screen as you scroll down (bootstrap library)
        $('#articleNavAffix').affix({
            offset: {
                top: 27, // - $('.header').height(),
                bottom: function () {
                    return (this.bottom = $('.footer').outerHeight(true) + 30);
                },
            },
        });

        //have the article nav update as you scroll down the page
        $('body').scrollspy({ target: '#articleNavSpy', });

        //make the page scroll to it's location instead of jump
        //catch all clicks on a tags
        $('a').click(function () {
            //check if it has a hash (i.e. if it's an anchor link)
            if (this.hash && (this.hash === $(this).attr('href'))) {
                var hash = this.hash.substr(1);
                var $toElement = $('[id=' + hash + ']');
                var toPosition = $toElement.offset().top;

                //scroll to element
                $('html').animate({
                    scrollTop: toPosition + 3,
                }, 1270);
            }
        });

        //do the same with urls with hash too (so on page load it will slide nicely)
        if (location.hash) {
            var hash = location.hash;
            window.scroll(0, 0);
            $('a[href=' + hash + ']').click();
        }
    });

    function setDocumentHeight() {
        var $doc = $('.doc');
        var $docWrapper = $('.doc-wrapper');
        var docMinHeight = (window.innerHeight - ($('.header').height() + $('.footer').height()));
        var docNav = $('.doc-nav-wrapper').height();
		var offsetToMakeFitPerfectly = -12;

        if ($doc.height() < docMinHeight || $doc.height() < docNav) {
            docNav > docMinHeight ?
                $docWrapper.height(docNav) :
				$docWrapper.height(docMinHeight + offsetToMakeFitPerfectly) ;
        }
    }

    function ToggleTree($elem) {
        if ($elem.hasClass('open')) {
            $elem
                .removeClass('open glyphicon-triangle-bottom')
                .addClass('glyphicon-triangle-right')
                .siblings('ul')
                .slideToggle(200);
            setTimeout(function () { setDocumentHeight(); }, 200);
        } else {
            $('.toctree-l1 > .glypicon.open')
                .removeClass('open glyphicon-triangle-bottom')
                .addClass('glyphicon-triangle-right')
                .siblings('ul')
                .slideToggle(200);
            $elem
                .addClass('glyphicon-triangle-bottom open')
                .siblings('ul')
                .slideToggle(200);
            setTimeout(function () { setDocumentHeight(); }, 200);
        }
    }

} (jQuery));

// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
(function($,sr){
  var debounce = function (func, threshold, execAsap) {
      var timeout;

      return function debounced () {
          var obj = this, args = arguments;
          function delayed () {
              if (!execAsap) {
                  func.apply(obj, args);
              }

              timeout = null;
          }

          if (timeout){
              clearTimeout(timeout);
          }
          else if (execAsap){
              func.apply(obj, args);
          }

          timeout = setTimeout(delayed, threshold || 100);
      };
  };
  // smartresize
  jQuery.fn[sr] = function(fn){  return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };

})(jQuery,'smartresize');<commit_msg>DOC-12 Fit and Finish Hide the sidebar title if there are no document sub-sections<commit_after>/* global jQuery */

(function ($) {
    'use strict';

    $(document).ready(function () {

        $('.toctree-l1')
            .has('.toctree-l2')
            .prepend('');

        // Toggle global nav on mobile
        $('.navbar-toggle').click(function () {
            var $this = $(this);

            if ($this.attr('aria-expanded') === 'false') {
                $this.attr('aria-expanded', true);
            } else {
                $this.attr('aria-expanded', false);
            }

            $('.nav-main-level').slideToggle(200);
        });

        // Toggle subnav on mobile
        $('.dropdown-toggle').click(function () {
            var $this = $(this);

            if ($this.attr('aria-expanded') === 'false') {
                $this.attr('aria-expanded', true);
            } else {
                $this.attr('aria-expanded', false);
            }

            $this.siblings('.nav-sub-level').slideToggle(200);
        });

        // open sub nav on mobile
        $('.doc-main-body').click(function () {
            if ($(window).width() < 992) {
                $('.doc-main-body').addClass('open');
            }
        });

        // close sub nav on mobile
        $('.doc-wrapper').click(function (e) {
            e.stopPropagation();

            if ($(window).width() < 992) {
                $('.doc-btn.open').click();
                $('.doc-main-body').removeClass('open');
            }
        });

        // open subnav for documentation nav
        $('.toctree-l1 > .glyphicon').click(function () {
            ToggleTree($(this));
        });

        // open subnav for current page
        $('.toctree-l1.current')
            .has('.toctree-l2')
            .children('.glyphicon')
            .click();

        // set the height of the documentation page when there isn't enough content to fill the page
        setDocumentHeight();

        // and check to do the same on resize
        $(window).smartresize(function () {
            setTimeout(function () { setDocumentHeight(); }, 270);
        });

        // adjust the HTML rendered by Sphinx for the TOC
        $('.article-nav-wrapper > ul')
            .attr({
                id: 'articleNavAffix',
                class: 'affix-top'
            })
            .children('li')
            .children()
            .wrapAll('

')
            .siblings('ul')
            .addClass('nav nav-tabs article-nav');

        $('#articleNavSpy > a').text('In this article:');

        // If there are no document sections, then hide the nav
        $('#articleNavSpy:not(:has(ul))').hide();

        // affix the article nav to the screen as you scroll down (bootstrap library)
        $('#articleNavAffix').affix({
            offset: {
                top: 27, // - $('.header').height(),
                bottom: function () {
                    return (this.bottom = $('.footer').outerHeight(true) + 30);
                },
            },
        });

        //have the article nav update as you scroll down the page
        $('body').scrollspy({ target: '#articleNavSpy', });

        //make the page scroll to it's location instead of jump
        //catch all clicks on a tags
        $('a').click(function () {
            //check if it has a hash (i.e. if it's an anchor link)
            if (this.hash && (this.hash === $(this).attr('href'))) {
                var hash = this.hash.substr(1);
                var $toElement = $('[id=' + hash + ']');
                var toPosition = $toElement.offset().top;

                //scroll to element
                $('html').animate({
                    scrollTop: toPosition + 3,
                }, 1270);
            }
        });

        //do the same with urls with hash too (so on page load it will slide nicely)
        if (location.hash) {
            var hash = location.hash;
            window.scroll(0, 0);
            $('a[href=' + hash + ']').click();
        }
    });

    function setDocumentHeight() {
        var $doc = $('.doc');
        var $docWrapper = $('.doc-wrapper');
        var docMinHeight = (window.innerHeight - ($('.header').height() + $('.footer').height()));
        var docNav = $('.doc-nav-wrapper').height();
		var offsetToMakeFitPerfectly = -12;

        if ($doc.height() < docMinHeight || $doc.height() < docNav) {
            docNav > docMinHeight ?
                $docWrapper.height(docNav) :
				$docWrapper.height(docMinHeight + offsetToMakeFitPerfectly) ;
        }
    }

    function ToggleTree($elem) {
        if ($elem.hasClass('open')) {
            $elem
                .removeClass('open glyphicon-triangle-bottom')
                .addClass('glyphicon-triangle-right')
                .siblings('ul')
                .slideToggle(200);
            setTimeout(function () { setDocumentHeight(); }, 200);
        } else {
            $('.toctree-l1 > .glypicon.open')
                .removeClass('open glyphicon-triangle-bottom')
                .addClass('glyphicon-triangle-right')
                .siblings('ul')
                .slideToggle(200);
            $elem
                .addClass('glyphicon-triangle-bottom open')
                .siblings('ul')
                .slideToggle(200);
            setTimeout(function () { setDocumentHeight(); }, 200);
        }
    }

} (jQuery));

// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
(function($,sr){
  var debounce = function (func, threshold, execAsap) {
      var timeout;

      return function debounced () {
          var obj = this, args = arguments;
          function delayed () {
              if (!execAsap) {
                  func.apply(obj, args);
              }

              timeout = null;
          }

          if (timeout){
              clearTimeout(timeout);
          }
          else if (execAsap){
              func.apply(obj, args);
          }

          timeout = setTimeout(delayed, threshold || 100);
      };
  };
  // smartresize
  jQuery.fn[sr] = function(fn){  return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };

})(jQuery,'smartresize');<|endoftext|>


<commit_before>"use strict";

var EXPORTED_SYMBOLS = [];

Components.utils.import("chrome://BeQuiet/content/ns.jsm");

BeQuiet.Iterable = function(iterator) {

	let self = this;

	self.iterator = iterator;

	/**
	 * Performs a filter, returning an Iterable that will skip over any elements
	 * not selected by the predicate.
	 *
	 * @param predicate
	 *            A function that returns true if the item should be included
	 */
	self.filter = function(predicate) {
		return new BeQuiet.Iterable(new function() {
			for(let item of self.iterator)
				if(predicate(item))
					yield item;
		});
	};

	/**
	 * Peforms a map, returning an Iterable that will apply the mapping function
	 * to each item while iterating, yielding the mapped value.
	 *
	 * @param func
	 *            A function that maps an input parameter to an output parameter
	 */
	self.map = function(func) {
		return new BeQuiet.Iterable(new function() {
			for(let item of self.iterator)
				yield func(item);
		});
	};

	/**
	 * Iterates over the values, performing a function for each value.
	 *
	 * @param func
	 *            A function to apply on each element
	 */
	self.forEach = function(func) {
		for(let item of self.iterator)
			func(item);
	};

	/**
	 * @param func
	 *            A function(current, previous) that performs a reduction over
	 *            the iterator
	 */
	self.reduce = function(func) {
		try {
			let previous = self.iterator.next();

			for(let current of self.iterator)
				previous = func(previous, current);

			return previous;
		} catch(e) {
			if (e instanceof StopIteration) {
				return null;			// There was nothing to reduce
			} else {
				throw e;
			}
		}
	};

	/**
	 * Returns the maximum value in the Iterable.
	 *
	 * @param comparator
	 *            A function(a, b) that returns less than 0, 0 or greater than 0
	 *            as a is less than, equal to or greater than b
	 */
	self.max = function(comparator) {
		return self.reduce( (p, c) => comparator(p, c) > 0 ? p : c );
	};

	/**
	 * Returns the minimum value in the Iterable.
	 *
	 * @param comparator
	 *            A function(a, b) that returns less than 0, 0 or greater than 0
	 *            as a is less than, equal to or greater than b
	 */
	self.min = function(comparator) {
		return self.reduce( (p, c) => comparator(p, c) < 0 ? p : c );
	};

	/**
	 * @return The first element in the Iterable
	 */
	self.first = function() {
		try {
			return self.iterator.next();
		} catch(e) {
			return null;
		}
	};

	/**
	 * @return An array containing the items in the iterator
	 */
	self.toArray = function() {
		let items = [];

		for(let item of self.iterator)
			items.push(item);

		return items;
	};

	/**
	 * @return A Set containing the unique items in the iterator
	 */
	self.toSet = function() {
		return new Set(self.iterator);
	};
};

/**
 * Creates an Iterable from one or more iterators.
 */
BeQuiet.Iterable.from = function() {
	let args = arguments;

	if(args.length == 0)
		throw new ReferenceError("Need an iterator to operate on");
	else if(args.length == 1)
		return new BeQuiet.Iterable(args[0]);
	else
		return new BeQuiet.Iterable(new function() {
			for(let i=0; i<args.length; i++)
				for(let item of args[i])
					yield item;
		});
};<commit_msg>Changed iterable to avoid creating closures.<commit_after>"use strict";

var EXPORTED_SYMBOLS = [];

Components.utils.import("chrome://BeQuiet/content/ns.jsm");

BeQuiet.Iterable = function(iterator) {
	this.iterator = iterator;
};

BeQuiet.Iterable.prototype = {
    /**
     * Performs a filter, returning an Iterable that will skip over any elements
     * not selected by the predicate.
     *
     * @param predicate
     *            A function that returns true if the item should be included
     */
    filter: function(predicate) {
        let iter = this.iterator;

        return new BeQuiet.Iterable(new function() {
            for(let item of iter)
                if(predicate(item))
                    yield item;
        });
    },

    /**
     * Peforms a map, returning an Iterable that will apply the mapping function
     * to each item while iterating, yielding the mapped value.
     *
     * @param func
     *            A function that maps an input parameter to an output parameter
     */
    map: function(func) {
        let iter = this.iterator;

        return new BeQuiet.Iterable(new function() {
            for(let item of iter)
                yield func(item);
        });
    },

    /**
     * Iterates over the values, performing a function for each value.
     *
     * @param func
     *            A function to apply on each element
     */
    forEach: function(func) {
        for(let item of this.iterator)
            func(item);
    },

    /**
     * @param func
     *            A function(current, previous) that performs a reduction over
     *            the iterator
     */
    reduce: function(func) {
        try {
            let previous = this.iterator.next();

            for(let current of this.iterator)
                previous = func(previous, current);

            return previous;
        } catch(e) {
            if (e instanceof StopIteration) {
                return null;            // There was nothing to reduce
            } else {
                throw e;
            }
        }
    },

    /**
     * Returns the maximum value in the Iterable.
     *
     * @param comparator
     *            A function(a, b) that returns less than 0, 0 or greater than 0
     *            as a is less than, equal to or greater than b
     */
    max: function(comparator) {
        return this.reduce( (p, c) => comparator(p, c) > 0 ? p : c );
    },

    /**
     * Returns the minimum value in the Iterable.
     *
     * @param comparator
     *            A function(a, b) that returns less than 0, 0 or greater than 0
     *            as a is less than, equal to or greater than b
     */
    min:function(comparator) {
        return this.reduce( (p, c) => comparator(p, c) < 0 ? p : c );
    },

    /**
     * @return The first element in the Iterable
     */
    first: function() {
        for(let item of this.iterator)
            return item;

        return null;
    },

    /**
     * @return An array containing the items in the iterator
     */
    toArray: function() {
        let items = [];

        for(let item of this.iterator)
            items.push(item);

        return items;
    },

    /**
     * @return A Set containing the unique items in the iterator
     */
    toSet: function() {
        return new Set(this.iterator);
    }
};

/**
 * Creates an Iterable from one or more iterators.
 */
BeQuiet.Iterable.from = function() {
	let args = arguments;

	if(args.length == 0)
		throw new ReferenceError("Need an iterator to operate on");
	else if(args.length == 1)
		return new BeQuiet.Iterable(args[0]);
	else
		return new BeQuiet.Iterable(new function() {
			for(let i=0; i<args.length; i++)
				for(let item of args[i])
					yield item;
		});
};<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict'

const program = require('commander')
const chalk = require('chalk')
const Table = require('cli-table2')
const miteApi = require('mite-api')

const pkg = require('./../package.json')
const config = require('./config.js')

function durationFromMinutes(minutes) {
  return (new Date(minutes * 60 * 1000)).toISOString().substr(11, 5)
}

// set a default period argument if not set
if (!process.argv[2] || process.argv[2].substr(0, 1) === '-' && process.argv[2] !== '--help') {
  process.argv.splice(2, 0, 'today')
}

program
  .version(pkg.version)
  .description('list time entries')
  .arguments('<period>')
  .option(
    '--project_id <project_id>',
    'project id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '--customer_id <customer_id>',
    'customer id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '--service_id <service_id>',
    'service id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '-s --search <query>',
    'search within the notes, to filter by multiple criteria connected with OR use comma-separated query values',
    ((val) => {
      if (typeof val === 'string') {
        return val.split(/\s*,\s*/)
      }
      return val
    })
  )
  .option(
    '-l, --limit <limit>',
    'numeric number of items to return (default 1000)',
    1000,
    ((val) => parseInt(val, 10))
  )
  .action((period) => {
    const mite = miteApi(config.get())
    const opts = {
      at: period,
      customer_id: program.customer_id,
      limit: program.limit,
      note: program.search,
      project_id: program.project_id,
      service_id: program.service_id,
    }

    mite.getTimeEntries(opts, (err, results) => {
      if (err) {
        throw err;
      }

      const table = new Table({
        wordWrap: true,
        colWidths: [null, null, null, null, null, null, null, 50],
        colAligns: ['right', 'right', null, 'right', 'right', 'right', null],
        head: ['#', 'id', 'date', 'project', 'duration', 'revenue', 'service', 'note']
      })

      // format each time entry and add it to the report table
      Array.prototype.push.apply(table, results.map((data, index) => {
        const timeEntry = data.time_entry;
        let minutes = timeEntry.minutes
        // detect time entries that are tracked (running) and add an indicator
        // to the table
        if (timeEntry.tracking) {
          minutes = timeEntry.tracking.minutes;
        }
        let duration = durationFromMinutes(minutes)
        // add a lock symbol to the duration when the time entry cannot be edited
        if (timeEntry.locked) {
          duration = '🔒 ' + duration;
        }
        if (timeEntry.tracking) {
          duration = '▶ ' + duration;
        }
        let row = [
          index + 1,
          timeEntry.id,
          timeEntry.date_at,
          timeEntry.project_name,
          duration,
          (timeEntry.revenue / 100).toFixed(2),
          timeEntry.service_name,
          timeEntry.note
        ]
        if (timeEntry.tracking) {
          row = row.map((v) => chalk.yellow(v))
        }
        if (timeEntry.locked) {
          row = row.map((v) => chalk.grey(v))
        }
        return row;
      }))

      const minutesTotal = results.reduce((sum, cur) => {
        return sum + cur.time_entry.minutes;
      }, 0)
      const revenueTotal = results.reduce((sum, cur) => {
        return sum + cur.time_entry.revenue;
      }, 0)

      // sum up everything as last row
      table.push([
        null,
        null,
        null,
        chalk.bold(durationFromMinutes(minutesTotal)),
        chalk.bold((revenueTotal / 100).toFixed(2))
      ])

      // show list to choose from
      console.log(table.toString())
    }) // get time entries

  })
  .parse(process.argv)
<commit_msg>fix(list): revenue & duration order<commit_after>#!/usr/bin/env node
'use strict'

const program = require('commander')
const chalk = require('chalk')
const Table = require('cli-table2')
const miteApi = require('mite-api')

const pkg = require('./../package.json')
const config = require('./config.js')

function durationFromMinutes(minutes) {
  return (new Date(minutes * 60 * 1000)).toISOString().substr(11, 5)
}

// set a default period argument if not set
if (!process.argv[2] || process.argv[2].substr(0, 1) === '-' && process.argv[2] !== '--help') {
  process.argv.splice(2, 0, 'today')
}

program
  .version(pkg.version)
  .description('list time entries')
  .arguments('<period>')
  .option(
    '--project_id <project_id>',
    'project id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '--customer_id <customer_id>',
    'customer id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '--service_id <service_id>',
    'service id, can be either a single ID, or multiple comma-separated IDs.'
  )
  .option(
    '-s --search <query>',
    'search within the notes, to filter by multiple criteria connected with OR use comma-separated query values',
    ((val) => {
      if (typeof val === 'string') {
        return val.split(/\s*,\s*/)
      }
      return val
    })
  )
  .option(
    '-l, --limit <limit>',
    'numeric number of items to return (default 1000)',
    1000,
    ((val) => parseInt(val, 10))
  )
  .action((period) => {
    const mite = miteApi(config.get())
    const opts = {
      at: period,
      customer_id: program.customer_id,
      limit: program.limit,
      note: program.search,
      project_id: program.project_id,
      service_id: program.service_id,
    }

    mite.getTimeEntries(opts, (err, results) => {
      if (err) {
        throw err;
      }

      const table = new Table({
        wordWrap: true,
        colWidths: [null, null, null, null, null, null, null, 50],
        colAligns: ['right', 'right', null, 'right', 'right', 'right', null],
        head: ['#', 'id', 'date', 'project', 'duration', 'revenue', 'service', 'note']
      })

      // format each time entry and add it to the report table
      Array.prototype.push.apply(table, results.map((data, index) => {
        const timeEntry = data.time_entry;
        let minutes = timeEntry.minutes
        // detect time entries that are tracked (running) and add an indicator
        // to the table
        if (timeEntry.tracking) {
          minutes = timeEntry.tracking.minutes;
        }
        let duration = durationFromMinutes(minutes)
        // add a lock symbol to the duration when the time entry cannot be edited
        if (timeEntry.locked) {
          duration = '🔒 ' + duration;
        }
        if (timeEntry.tracking) {
          duration = '▶ ' + duration;
        }
        let row = [
          index + 1,
          timeEntry.id,
          timeEntry.date_at,
          timeEntry.project_name,
          duration,
          (timeEntry.revenue / 100).toFixed(2),
          timeEntry.service_name,
          timeEntry.note
        ]
        if (timeEntry.tracking) {
          row = row.map((v) => chalk.yellow(v))
        }
        if (timeEntry.locked) {
          row = row.map((v) => chalk.grey(v))
        }
        return row;
      }))

      const minutesTotal = results.reduce((sum, cur) => {
        return sum + cur.time_entry.minutes;
      }, 0)
      const revenueTotal = results.reduce((sum, cur) => {
        return sum + cur.time_entry.revenue;
      }, 0)

      // sum up everything as last row
      table.push([
        null,
        null,
        null,
        null,
        chalk.bold(durationFromMinutes(minutesTotal)),
        chalk.bold((revenueTotal / 100).toFixed(2))
      ])

      // show list to choose from
      console.log(table.toString())
    }) // get time entries

  })
  .parse(process.argv)
<|endoftext|>


<commit_before>module.exports = {
  set:set,
  get:get,
  has:has,
  bind:bind,
  method:method,
  log:log,
  array:array,
  succeed:succeed,
  fail:fail
};

function get (obj, key) {
  for (var i in obj) if (i.toLowerCase() === key.toLowerCase()) return obj[i];
  return undefined;
};

function set (obj, key, val) {
  for (var i in obj) if (i.toLowerCase() === key.toLowerCase()) return obj[i] = val;
  return obj[key] = val;
};

function has (obj, key) {
  for (var i in obj) if (obj.hasOwnProperty(i) && i.toLowerCase === key.toLowerCase()) return true;
  return false;
};

function bind (fn, o) {
  var args = array(arguments, 2);
  return function () {
    return fn.apply(o, args.concat(array(arguments)));
  };
};

function method (o, m) {
  var args = array(arguments, 2);
  if (!o) {
    throw new Error("invalid object: "+o);
  }
  return function () {
    return o[m].apply(o, args.concat(array(arguments)));
  };
};

function log (msg, pref) {
  pref = (pref && ": " + pref) || "";
  if (msg) msg = "npm"+pref+": "+msg;
  process.stdio.writeError(msg+"\n");
};

function array (arr, n) {
  return Array.prototype.slice.call(arr,n || 0);
};

function succeed (p) {
  return method(p, "emitSuccess", array(arguments,1));
};
function fail (p) {
  return method(p, "emitError", array(arguments,1));
}<commit_msg>Color the log messages a bit so they're easier to scan.<commit_after>module.exports = {
  set:set,
  get:get,
  has:has,
  bind:bind,
  method:method,
  log:log,
  array:array,
  succeed:succeed,
  fail:fail
};

function get (obj, key) {
  for (var i in obj) if (i.toLowerCase() === key.toLowerCase()) return obj[i];
  return undefined;
};

function set (obj, key, val) {
  for (var i in obj) if (i.toLowerCase() === key.toLowerCase()) return obj[i] = val;
  return obj[key] = val;
};

function has (obj, key) {
  for (var i in obj) if (obj.hasOwnProperty(i) && i.toLowerCase === key.toLowerCase()) return true;
  return false;
};

function bind (fn, o) {
  var args = array(arguments, 2);
  return function () {
    return fn.apply(o, args.concat(array(arguments)));
  };
};

function method (o, m) {
  var args = array(arguments, 2);
  if (!o) {
    throw new Error("invalid object: "+o);
  }
  return function () {
    return o[m].apply(o, args.concat(array(arguments)));
  };
};

function log (msg, pref) {
  pref = (pref && ": " + pref) || "";
  if (msg) msg = "\033[31mnpm\033[0m"+pref+": "+msg;
  process.stdio.writeError(msg+"\n");
};

function array (arr, n) {
  return Array.prototype.slice.call(arr,n || 0);
};

function succeed (p) {
  return method(p, "emitSuccess", array(arguments,1));
};
function fail (p) {
  return method(p, "emitError", array(arguments,1));
}<|endoftext|>


<commit_before>module.exports = c => `module.exports = ${JSON.stringify(c + "toml")}`;
<commit_msg>fix windows issue<commit_after>module.exports = c => `module.exports = ${JSON.stringify(c.trim() + "\ntoml")}`;
<|endoftext|>


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

var Chat = {
	settings: {
		assetsPath: "assets/chat/",
		pushTime: 2500
	},
	doPush: function() {
		var elements = document.getElementsByTagName("x-chat-msg");
		var i = 0;
		while(i < elements.length) {
			var top = parseInt(elements[i].style.top.replace("px", ""))-30;

			if(top < -100) {
				elements[i].parentElement.removeChild(elements[i]);

			} else {
				elements[i].style.top = top;
			}
			i++;
		}
	},
	timeInitiated: false,
	create: function(name, message, left) {
		this.doPush();

		message = escapeHtmlEntities(message);

		var chatElement = document.createElement("x-chat-msg");
		chatElement.id = "chat-msg-"+Math.random();

		chatElement.style.fontFamily = "Verdana";
		chatElement.style.fontSize = "10px";
		chatElement.style.position = "absolute";
		chatElement.style.left = left;
		chatElement.style.top = 200;
		chatElement.style.transitionProperty = "top";
		chatElement.style.transitionDuration = "1s, 1s";
		chatElement.style.transitionDelay = "0s, 1s";

		var chatLeft = document.createElement("chat-name");
		var chatRight = document.createElement("chat-msg");

		chatLeft.style.float = "left";
		chatLeft.style.height = "23px";
		chatLeft.style.lineHeight = "23px";
		chatLeft.style.paddingLeft ="10px";
		chatLeft.style.paddingRight ="5px";
		chatLeft.style.backgroundImage = "url('"+this.settings.assetsPath+"name-container.png')";
		chatLeft.innerHTML = ""+name+"";

		chatRight.style.float = "left";
		chatRight.style.height = "23px";
		chatRight.style.lineHeight = "23px";
		chatRight.style.paddingLeft ="10px";
		chatRight.style.paddingRight ="15px";
		chatRight.style.backgroundImage = "url('"+this.settings.assetsPath+"msg-container.png')";
		chatRight.style.backgroundPosition  = "right bottom";
		chatRight.innerHTML = message;

		chatElement.appendChild(chatLeft);
		chatElement.appendChild(chatRight);

		if(!this.timeInitiated) {
			this.timeInitiated = true;
			setInterval(function() { Chat.doPush(); }, this.settings.pushTime);
		}

		return chatElement;
	}

};

document.registerElement("x-chat-msg");

// from here: http://stackoverflow.com/a/1354715/2376468
if(typeof escapeHtmlEntities == 'undefined') {
	escapeHtmlEntities = function(text) {
		return text.replace(/[\u00A0-\u2666<>\&]/g, function(c) {
			return '&' +
				(escapeHtmlEntities.entityTable[c.charCodeAt(0)] || '#' + c.charCodeAt(0)) + ';';
		});
	};
	escapeHtmlEntities.entityTable = {
		34: 'quot',
		38: 'amp',
		39: 'apos',
		60: 'lt',
		62: 'gt',
		160: 'nbsp',
		161: 'iexcl',
		162: 'cent',
		163: 'pound',
		164: 'curren',
		165: 'yen',
		166: 'brvbar',
		167: 'sect',
		168: 'uml',
		169: 'copy',
		170: 'ordf',
		171: 'laquo',
		172: 'not',
		173: 'shy',
		174: 'reg',
		175: 'macr',
		176: 'deg',
		177: 'plusmn',
		178: 'sup2',
		179: 'sup3',
		180: 'acute',
		181: 'micro',
		182: 'para',
		183: 'middot',
		184: 'cedil',
		185: 'sup1',
		186: 'ordm',
		187: 'raquo',
		188: 'frac14',
		189: 'frac12',
		190: 'frac34',
		191: 'iquest',
		192: 'Agrave',
		193: 'Aacute',
		194: 'Acirc',
		195: 'Atilde',
		196: 'Auml',
		197: 'Aring',
		198: 'AElig',
		199: 'Ccedil',
		200: 'Egrave',
		201: 'Eacute',
		202: 'Ecirc',
		203: 'Euml',
		204: 'Igrave',
		205: 'Iacute',
		206: 'Icirc',
		207: 'Iuml',
		208: 'ETH',
		209: 'Ntilde',
		210: 'Ograve',
		211: 'Oacute',
		212: 'Ocirc',
		213: 'Otilde',
		214: 'Ouml',
		215: 'times',
		216: 'Oslash',
		217: 'Ugrave',
		218: 'Uacute',
		219: 'Ucirc',
		220: 'Uuml',
		221: 'Yacute',
		222: 'THORN',
		223: 'szlig',
		224: 'agrave',
		225: 'aacute',
		226: 'acirc',
		227: 'atilde',
		228: 'auml',
		229: 'aring',
		230: 'aelig',
		231: 'ccedil',
		232: 'egrave',
		233: 'eacute',
		234: 'ecirc',
		235: 'euml',
		236: 'igrave',
		237: 'iacute',
		238: 'icirc',
		239: 'iuml',
		240: 'eth',
		241: 'ntilde',
		242: 'ograve',
		243: 'oacute',
		244: 'ocirc',
		245: 'otilde',
		246: 'ouml',
		247: 'divide',
		248: 'oslash',
		249: 'ugrave',
		250: 'uacute',
		251: 'ucirc',
		252: 'uuml',
		253: 'yacute',
		254: 'thorn',
		255: 'yuml',
		402: 'fnof',
		913: 'Alpha',
		914: 'Beta',
		915: 'Gamma',
		916: 'Delta',
		917: 'Epsilon',
		918: 'Zeta',
		919: 'Eta',
		920: 'Theta',
		921: 'Iota',
		922: 'Kappa',
		923: 'Lambda',
		924: 'Mu',
		925: 'Nu',
		926: 'Xi',
		927: 'Omicron',
		928: 'Pi',
		929: 'Rho',
		931: 'Sigma',
		932: 'Tau',
		933: 'Upsilon',
		934: 'Phi',
		935: 'Chi',
		936: 'Psi',
		937: 'Omega',
		945: 'alpha',
		946: 'beta',
		947: 'gamma',
		948: 'delta',
		949: 'epsilon',
		950: 'zeta',
		951: 'eta',
		952: 'theta',
		953: 'iota',
		954: 'kappa',
		955: 'lambda',
		956: 'mu',
		957: 'nu',
		958: 'xi',
		959: 'omicron',
		960: 'pi',
		961: 'rho',
		962: 'sigmaf',
		963: 'sigma',
		964: 'tau',
		965: 'upsilon',
		966: 'phi',
		967: 'chi',
		968: 'psi',
		969: 'omega',
		977: 'thetasym',
		978: 'upsih',
		982: 'piv',
		8226: 'bull',
		8230: 'hellip',
		8242: 'prime',
		8243: 'Prime',
		8254: 'oline',
		8260: 'frasl',
		8472: 'weierp',
		8465: 'image',
		8476: 'real',
		8482: 'trade',
		8501: 'alefsym',
		8592: 'larr',
		8593: 'uarr',
		8594: 'rarr',
		8595: 'darr',
		8596: 'harr',
		8629: 'crarr',
		8656: 'lArr',
		8657: 'uArr',
		8658: 'rArr',
		8659: 'dArr',
		8660: 'hArr',
		8704: 'forall',
		8706: 'part',
		8707: 'exist',
		8709: 'empty',
		8711: 'nabla',
		8712: 'isin',
		8713: 'notin',
		8715: 'ni',
		8719: 'prod',
		8721: 'sum',
		8722: 'minus',
		8727: 'lowast',
		8730: 'radic',
		8733: 'prop',
		8734: 'infin',
		8736: 'ang',
		8743: 'and',
		8744: 'or',
		8745: 'cap',
		8746: 'cup',
		8747: 'int',
		8756: 'there4',
		8764: 'sim',
		8773: 'cong',
		8776: 'asymp',
		8800: 'ne',
		8801: 'equiv',
		8804: 'le',
		8805: 'ge',
		8834: 'sub',
		8835: 'sup',
		8836: 'nsub',
		8838: 'sube',
		8839: 'supe',
		8853: 'oplus',
		8855: 'otimes',
		8869: 'perp',
		8901: 'sdot',
		8968: 'lceil',
		8969: 'rceil',
		8970: 'lfloor',
		8971: 'rfloor',
		9001: 'lang',
		9002: 'rang',
		9674: 'loz',
		9824: 'spades',
		9827: 'clubs',
		9829: 'hearts',
		9830: 'diams',
		338: 'OElig',
		339: 'oelig',
		352: 'Scaron',
		353: 'scaron',
		376: 'Yuml',
		710: 'circ',
		732: 'tilde',
		8194: 'ensp',
		8195: 'emsp',
		8201: 'thinsp',
		8204: 'zwnj',
		8205: 'zwj',
		8206: 'lrm',
		8207: 'rlm',
		8211: 'ndash',
		8212: 'mdash',
		8216: 'lsquo',
		8217: 'rsquo',
		8218: 'sbquo',
		8220: 'ldquo',
		8221: 'rdquo',
		8222: 'bdquo',
		8224: 'dagger',
		8225: 'Dagger',
		8240: 'permil',
		8249: 'lsaquo',
		8250: 'rsaquo',
		8364: 'euro'
	};
};<commit_msg>Adding zIndex setting<commit_after>/**
 * CokeStudios Chat
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Mark Hughes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

var Chat = {
	settings: {
		assetsPath: "assets/chat/",
		pushTime: 2500,
		zIndex: 9000,
	},
	doPush: function() {
		var elements = document.getElementsByTagName("x-chat-msg");
		var i = 0;
		while(i < elements.length) {
			var top = parseInt(elements[i].style.top.replace("px", ""))-30;

			if(top < -100) {
				elements[i].parentElement.removeChild(elements[i]);

			} else {
				elements[i].style.top = top;
			}
			i++;
		}
	},
	timeInitiated: false,
	create: function(name, message, left) {
		this.doPush();

		message = escapeHtmlEntities(message);

		var chatElement = document.createElement("x-chat-msg");
		chatElement.id = "chat-msg-"+Math.random();

		chatElement.style.fontFamily = "Verdana";
		chatElement.style.fontSize = "10px";
		chatElement.style.position = "absolute";
		chatElement.style.left = left;
		chatElement.style.top = 200;
		chatElement.style.zIndex = this.settings.zIndex;
		chatElement.style.transitionProperty = "top";
		chatElement.style.transitionDuration = "1s, 1s";
		chatElement.style.transitionDelay = "0s, 1s";

		var chatLeft = document.createElement("chat-name");
		var chatRight = document.createElement("chat-msg");

		chatLeft.style.float = "left";
		chatLeft.style.height = "23px";
		chatLeft.style.lineHeight = "23px";
		chatLeft.style.paddingLeft ="10px";
		chatLeft.style.paddingRight ="5px";
		chatLeft.style.backgroundImage = "url('"+this.settings.assetsPath+"name-container.png')";
		chatLeft.innerHTML = ""+name+"";

		chatRight.style.float = "left";
		chatRight.style.height = "23px";
		chatRight.style.lineHeight = "23px";
		chatRight.style.paddingLeft ="10px";
		chatRight.style.paddingRight ="15px";
		chatRight.style.backgroundImage = "url('"+this.settings.assetsPath+"msg-container.png')";
		chatRight.style.backgroundPosition  = "right bottom";
		chatRight.innerHTML = message;

		chatElement.appendChild(chatLeft);
		chatElement.appendChild(chatRight);

		if(!this.timeInitiated) {
			this.timeInitiated = true;
			setInterval(function() { Chat.doPush(); }, this.settings.pushTime);
		}

		return chatElement;
	}

};

document.registerElement("x-chat-msg");

// from here: http://stackoverflow.com/a/1354715/2376468
if(typeof escapeHtmlEntities == 'undefined') {
	escapeHtmlEntities = function(text) {
		return text.replace(/[\u00A0-\u2666<>\&]/g, function(c) {
			return '&' +
				(escapeHtmlEntities.entityTable[c.charCodeAt(0)] || '#' + c.charCodeAt(0)) + ';';
		});
	};
	escapeHtmlEntities.entityTable = {
		34: 'quot',
		38: 'amp',
		39: 'apos',
		60: 'lt',
		62: 'gt',
		160: 'nbsp',
		161: 'iexcl',
		162: 'cent',
		163: 'pound',
		164: 'curren',
		165: 'yen',
		166: 'brvbar',
		167: 'sect',
		168: 'uml',
		169: 'copy',
		170: 'ordf',
		171: 'laquo',
		172: 'not',
		173: 'shy',
		174: 'reg',
		175: 'macr',
		176: 'deg',
		177: 'plusmn',
		178: 'sup2',
		179: 'sup3',
		180: 'acute',
		181: 'micro',
		182: 'para',
		183: 'middot',
		184: 'cedil',
		185: 'sup1',
		186: 'ordm',
		187: 'raquo',
		188: 'frac14',
		189: 'frac12',
		190: 'frac34',
		191: 'iquest',
		192: 'Agrave',
		193: 'Aacute',
		194: 'Acirc',
		195: 'Atilde',
		196: 'Auml',
		197: 'Aring',
		198: 'AElig',
		199: 'Ccedil',
		200: 'Egrave',
		201: 'Eacute',
		202: 'Ecirc',
		203: 'Euml',
		204: 'Igrave',
		205: 'Iacute',
		206: 'Icirc',
		207: 'Iuml',
		208: 'ETH',
		209: 'Ntilde',
		210: 'Ograve',
		211: 'Oacute',
		212: 'Ocirc',
		213: 'Otilde',
		214: 'Ouml',
		215: 'times',
		216: 'Oslash',
		217: 'Ugrave',
		218: 'Uacute',
		219: 'Ucirc',
		220: 'Uuml',
		221: 'Yacute',
		222: 'THORN',
		223: 'szlig',
		224: 'agrave',
		225: 'aacute',
		226: 'acirc',
		227: 'atilde',
		228: 'auml',
		229: 'aring',
		230: 'aelig',
		231: 'ccedil',
		232: 'egrave',
		233: 'eacute',
		234: 'ecirc',
		235: 'euml',
		236: 'igrave',
		237: 'iacute',
		238: 'icirc',
		239: 'iuml',
		240: 'eth',
		241: 'ntilde',
		242: 'ograve',
		243: 'oacute',
		244: 'ocirc',
		245: 'otilde',
		246: 'ouml',
		247: 'divide',
		248: 'oslash',
		249: 'ugrave',
		250: 'uacute',
		251: 'ucirc',
		252: 'uuml',
		253: 'yacute',
		254: 'thorn',
		255: 'yuml',
		402: 'fnof',
		913: 'Alpha',
		914: 'Beta',
		915: 'Gamma',
		916: 'Delta',
		917: 'Epsilon',
		918: 'Zeta',
		919: 'Eta',
		920: 'Theta',
		921: 'Iota',
		922: 'Kappa',
		923: 'Lambda',
		924: 'Mu',
		925: 'Nu',
		926: 'Xi',
		927: 'Omicron',
		928: 'Pi',
		929: 'Rho',
		931: 'Sigma',
		932: 'Tau',
		933: 'Upsilon',
		934: 'Phi',
		935: 'Chi',
		936: 'Psi',
		937: 'Omega',
		945: 'alpha',
		946: 'beta',
		947: 'gamma',
		948: 'delta',
		949: 'epsilon',
		950: 'zeta',
		951: 'eta',
		952: 'theta',
		953: 'iota',
		954: 'kappa',
		955: 'lambda',
		956: 'mu',
		957: 'nu',
		958: 'xi',
		959: 'omicron',
		960: 'pi',
		961: 'rho',
		962: 'sigmaf',
		963: 'sigma',
		964: 'tau',
		965: 'upsilon',
		966: 'phi',
		967: 'chi',
		968: 'psi',
		969: 'omega',
		977: 'thetasym',
		978: 'upsih',
		982: 'piv',
		8226: 'bull',
		8230: 'hellip',
		8242: 'prime',
		8243: 'Prime',
		8254: 'oline',
		8260: 'frasl',
		8472: 'weierp',
		8465: 'image',
		8476: 'real',
		8482: 'trade',
		8501: 'alefsym',
		8592: 'larr',
		8593: 'uarr',
		8594: 'rarr',
		8595: 'darr',
		8596: 'harr',
		8629: 'crarr',
		8656: 'lArr',
		8657: 'uArr',
		8658: 'rArr',
		8659: 'dArr',
		8660: 'hArr',
		8704: 'forall',
		8706: 'part',
		8707: 'exist',
		8709: 'empty',
		8711: 'nabla',
		8712: 'isin',
		8713: 'notin',
		8715: 'ni',
		8719: 'prod',
		8721: 'sum',
		8722: 'minus',
		8727: 'lowast',
		8730: 'radic',
		8733: 'prop',
		8734: 'infin',
		8736: 'ang',
		8743: 'and',
		8744: 'or',
		8745: 'cap',
		8746: 'cup',
		8747: 'int',
		8756: 'there4',
		8764: 'sim',
		8773: 'cong',
		8776: 'asymp',
		8800: 'ne',
		8801: 'equiv',
		8804: 'le',
		8805: 'ge',
		8834: 'sub',
		8835: 'sup',
		8836: 'nsub',
		8838: 'sube',
		8839: 'supe',
		8853: 'oplus',
		8855: 'otimes',
		8869: 'perp',
		8901: 'sdot',
		8968: 'lceil',
		8969: 'rceil',
		8970: 'lfloor',
		8971: 'rfloor',
		9001: 'lang',
		9002: 'rang',
		9674: 'loz',
		9824: 'spades',
		9827: 'clubs',
		9829: 'hearts',
		9830: 'diams',
		338: 'OElig',
		339: 'oelig',
		352: 'Scaron',
		353: 'scaron',
		376: 'Yuml',
		710: 'circ',
		732: 'tilde',
		8194: 'ensp',
		8195: 'emsp',
		8201: 'thinsp',
		8204: 'zwnj',
		8205: 'zwj',
		8206: 'lrm',
		8207: 'rlm',
		8211: 'ndash',
		8212: 'mdash',
		8216: 'lsquo',
		8217: 'rsquo',
		8218: 'sbquo',
		8220: 'ldquo',
		8221: 'rdquo',
		8222: 'bdquo',
		8224: 'dagger',
		8225: 'Dagger',
		8240: 'permil',
		8249: 'lsaquo',
		8250: 'rsaquo',
		8364: 'euro'
	};
};<|endoftext|>