<commit_before>/* eslint-disable import/no-extraneous-dependencies */
const spawn = require('cross-spawn');

const isWindows = process.platform.match(/^win/);

// Excludes Cucumber features marked with the "@cli" tag.
// CLI does not work on Windows:
// https://github.com/apiaryio/gavel-spec/issues/24
const tags = [isWindows && '~@cli'].filter(Boolean);

const args = [
  ...tags,
  '-r',
  'test/cucumber/support/',
  '-r',
  'test/cucumber/steps/',
  '-f',
  'pretty',
  'node_modules/gavel-spec/features/'
];

const cucumber = spawn('node_modules/.bin/cucumber-js', args);

cucumber.stdout.on('data', (data) => process.stdout.write(data));
cucumber.stderr.on('data', (data) => process.stderr.write(data));

cucumber.on('error', (err) => {
  console.error(err);
  process.exit(1);
});

cucumber.on('close', (code) => process.exit(code));
<commit_msg>fix: excludes "@cli" features on win platform<commit_after>/* eslint-disable import/no-extraneous-dependencies */
const spawn = require('cross-spawn');

const isWindows = process.platform.match(/^win/);

// Excludes Cucumber features marked with the "@cli" tag.
// CLI does not work on Windows:
// https://github.com/apiaryio/gavel-spec/issues/24
const tags = [isWindows && '-t ~@cli'].filter(Boolean);

const args = [
  ...tags,
  '-r',
  'test/cucumber/support/',
  '-r',
  'test/cucumber/steps/',
  '-f',
  'pretty',
  'node_modules/gavel-spec/features/'
];

const cucumber = spawn('node_modules/.bin/cucumber-js', args);

cucumber.stdout.on('data', (data) => process.stdout.write(data));
cucumber.stderr.on('data', (data) => process.stderr.write(data));

cucumber.on('error', (err) => {
  console.error(err);
  process.exit(1);
});

cucumber.on('close', (code) => process.exit(code));
<|endoftext|>


<commit_before>/**
    Framework for building object relational database apps

    Copyright (C) 2015  John Rogelstad
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

/*global window, f, m */
(function (f) {
  "use strict";

  var EDIT_MODE = 2,
    LIST_MODE = 1;

  // Calculate scroll bar width
  // http://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
  var scrWidth, inner, widthNoScroll, widthWithScroll,
    outer = document.createElement("div");

  outer.style.visibility = "hidden";
  outer.style.width = "100px";
  outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps

  document.body.appendChild(outer);

  widthNoScroll = outer.offsetWidth;
  // force scrollbars
  outer.style.overflow = "scroll";

  // add innerdiv
  inner = document.createElement("div");
  inner.style.width = "100%";
  outer.appendChild(inner);

  widthWithScroll = inner.offsetWidth;

  // remove divs
  outer.parentNode.removeChild(outer);
  scrWidth = widthNoScroll - widthWithScroll;

  // Define workbook view model
  f.viewModels.workbookViewModel = function (options) {
    var selection,
      sheet = options.sheet,
      frmroute = "/" + options.name + "/" + options.config[sheet].form.name,
      name = options.feather.toCamelCase(),
      vm = {};
    frmroute = frmroute.toSpinalCase();

    vm.config = function () {
      return options.config || {};
    };
    vm.models = f.models[name].list();
    vm.attrs = options.config[sheet].list.attrs || ["id"];
    vm.canSave = function (model) {
      var currentState = model.state.current()[0];
      return (currentState === "/Ready/New" ||
        currentState === "/Ready/Fetched/Dirty") &&
        model.isValid();
    };
    vm.goHome = function () {
      m.route("/home");
    };
    vm.goNextRow = function () {
      var list = vm.models(),
        model = vm.selection(),
        idx = list.indexOf(model) + 1;
      if (list.length > idx) {
        vm.select(list[idx]);
      }
    };
    vm.goPrevRow = function () {
      var list = vm.models(),
        model = vm.selection(),
        idx = list.indexOf(model) - 1;
      if (idx >= 0) {
        vm.select(list[idx]);
      }
    };
    vm.focusColumn = m.prop(vm.attrs[0]);
    vm.canSave = function () {
      return vm.models().some(function (model) {
        return model.canSave();
      });
    };
    vm.hasNoSelection = function () {
      return !selection;
    };
    vm.isSelected = function (model) {
      return selection === model;
    };
    vm.modelDelete = function () {
      // Delete now in list mode
      if (vm.mode() === LIST_MODE) {
        selection.delete(true).then(function () {
          vm.models().remove(selection);
        });
        return;
      }

      // Mork for deletion in edit mode
      selection.delete();
    };
    vm.mode = m.prop(LIST_MODE);
    vm.modelNew = function () {
      m.route(frmroute);
    };
    vm.modelOpen = function () {
      m.route(frmroute + "/" + selection.data.id());
    };
    vm.onkeydown = function (e) {
      var id,
        nav = function (name) {
          id = e.srcElement.id;
          // Navigate in desired direction
          m.startComputation();
          vm[name]();
          m.endComputation();
          // Set focus on the same cell we left
          m.startComputation();
          document.getElementById(id).focus();
          m.endComputation();
        };

      switch (e.key || e.keyIdentifier)
      {
      case "Up":
        nav("goPrevRow");
        break;
      case "Down":
        nav("goNextRow");
        break;
      }
    };
    vm.onscroll = function () {
      var rows = document.getElementById("rows"),
        header = document.getElementById("header");

      // Sync header position with table body position
      header.scrollLeft = rows.scrollLeft;
    };
    vm.scrollbarWidth = function () {
      return scrWidth;
    };
    vm.saveAll = function () {
      vm.models().forEach(function (model) {
        model.save().then(function() {
          if (model.state.current()[0] === "/Deleted") {
            vm.models().remove(model);
          }
        });
      });
    };
    vm.select = function (model) {
      if (selection !== model) {
        selection = model;
      }
      return selection;
    };
    vm.selection = function () {
      return selection;
    };
    vm.selectedTab = m.prop(options.sheet);
    vm.sheets = function () {
      return Object.keys(options.config || {});
    };
    vm.tabClicked = function (sheet) {
      var route = "/" + options.name + "/" + sheet;
      route = route.toSpinalCase();
      m.route(route);
    };
    vm.toggleMode = function () {
      var mode = vm.mode() === LIST_MODE ? EDIT_MODE : LIST_MODE;
      vm.mode(mode);
    };
    vm.toggleOpen = function (model) {
      selection = model;
      vm.modelOpen();
    };
    vm.toggleSelection = function (model) {
      if (selection === model && vm.mode() === LIST_MODE) {
        vm.select(undefined);
        return false;
      }
      vm.select(model);
      return true;
    };
    vm.undo = function () {
      if (selection) { selection.undo(); }
    };

    return vm;
  };

  // Define workbook component
  f.components.workbookDisplay = function (options) {
    var component = {};

    component.controller = function () {
      this.vm = f.viewModels.workbookViewModel(options);
    };

    component.view = function (ctrl) {
      var tbodyConfig, header, rows, tabs, view,
        vm = ctrl.vm;

      // Define scrolling behavior for table body
      tbodyConfig = function (e) {
        var tb = document.getElementById("toolbar"),
          hd = document.getElementById("header"),
          ts = document.getElementById("tabs"),
          mh = window.innerHeight - tb.clientHeight - hd.clientHeight - ts.clientHeight- 12;

        // Set fields table to scroll and toolbar to stay put
        document.documentElement.style.overflow = 'hidden';
        e.style.height = mh + "px";

        // Key down handler for up down movement
        e.addEventListener('keydown', vm.onkeydown);
      };

      // Build header
      header = (function () {
        var ths = vm.attrs.map(function (key) {
            return m("th", {
              style: {
                minWidth: "100px",
                maxWidth: "100px",
                whiteSpace: "nowrap",
                overflow: "hidden",
                textOverflow: "ellipsis"
              }
            },
              key.toProperCase(true));
          });

        // Front cap header navigation
        ths.unshift(m("th", {style: {minWidth: "16px"}}));

        // End cap on header for scrollbar
        ths.push(m("th", {
          style: {
            minWidth: vm.scrollbarWidth() + "px",
            maxWidth: vm.scrollbarWidth() + "px"
          }
        }));
        return m("tr", ths);
      }());

      // Build rows
      rows = vm.models().map(function (model) {
        var tds, row, thContent,
          mode = vm.mode(),
          color = "White",
          isSelected = vm.isSelected(model),
          d = model.data,
          rowOpts = {};

        // Build view row
        if (mode === LIST_MODE || !isSelected) {
          // Build cells
          tds = vm.attrs.map(function (col) {
            var cell,
              value = d[col](),
              hasLocale = value !== null &&
                typeof value.toLocaleString === "function";

            // Build cell
            cell = m("td", {
                style: {
                  minWidth: "100px",
                  maxWidth: "100px",
                  whiteSpace: "nowrap",
                  overflow: "hidden",
                  textOverflow: "ellipsis"
                }
              },
              hasLocale ? value.toLocaleString() : value);

            return cell;
          });

          // Build row
          if (isSelected) {
            color = "LightBlue";
          }

          rowOpts = {
            ondblclick: vm.toggleOpen.bind(this, model)
          };

        // Build editable row
        } else {
          // Build cells
          tds = vm.attrs.map(function (col) {
            var cell, cellOpts,
              id = "input" + col.toCamelCase(true);

            cellOpts = {
              id: id,
              onchange: m.withAttr("value", d[col]),
              value: d[col](),
              style: {
                minWidth: "100px",
                maxWidth: "100px"
              }
            };

            // Build cell
            cell = m("td", [
              m("input", cellOpts)
            ]);

            return cell;
          });
        }

        // Front cap header navigation
        if (model.state.current()[0] === "/Delete") {
          thContent = [m("i", {class:"fa fa-remove"})];
        } else if (model.canSave()) {
          thContent = [m("i", {class:"fa fa-check"})];
        } else {
          thContent = {
            style: {
              minWidth: "16px"
            }
          };
        }
        tds.unshift(m("th", thContent));

        // Build row
        rowOpts.onclick = vm.toggleSelection.bind(this, model);
        rowOpts.style = { backgroundColor: color };
        row = m("tr", rowOpts, tds);

        return row;
      });

      // Build tabs
      tabs = vm.sheets().map(function (sheet) {
        var tab,
          sconfig = vm.config()[sheet],
          label = sconfig.feather ?
            sheet : f.catalog.getFeather(sheet).plural;

        // Build tab
        tab = m("button[type=button]", {
          class: vm.selectedTab() === sheet ?
            "pure-button pure-button-active" : "pure-button",
          style: {
            borderTopLeftRadius: "0px",
            borderTopRightRadius: "0px"
          },
          onclick: vm.tabClicked.bind(this, sheet)
        }, label);

        return tab;
      });

      // Finally assemble the whole view
      view = m("form", {
        class: "pure-form"
      }, [
        m("div", {
            id: "toolbar"
          }, [
          m("button", {
            type: "button",
            class: "pure-button",
            style: { margin: "1px" },
            onclick: vm.goHome
          }, [m("i", {class:"fa fa-home"})], " Home"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === EDIT_MODE ? "none" : "inline-block"
            },
            onclick: vm.toggleMode
          }, [m("i", {class:"fa fa-pencil"})], " Edit"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === LIST_MODE ? "none" : "inline-block"
            },
            onclick: vm.toggleMode
          }, [m("i", {class:"fa fa-th-list"})], " List"),
            m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === LIST_MODE ? "none" : "inline-block"
            },
            onclick: vm.saveAll,
            disabled: !vm.canSave()
          }, [m("i", {class:"fa fa-save"})], " Save"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === EDIT_MODE ? "none" : "inline-block"
            },
            onclick: vm.modelOpen,
            disabled: vm.hasNoSelection()
          }, [m("i", {class:"fa fa-folder-open"})], " Open"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: { margin: "1px"},
            onclick: vm.modelNew
          }, [m("i", {class:"fa fa-plus-circle"})], " New"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: (vm.selection() && vm.selection().canSave()) ?
                "none" : "inline-block"
            },
            onclick: vm.modelDelete,
            disabled: vm.hasNoSelection()
          }, [m("i", {class:"fa fa-remove"})], " Delete"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: (vm.selection() && vm.selection().canSave()) ?
                "inline-block" : "none"
            },
            onclick: vm.undo
          }, [m("i", {class:"fa fa-undo"})], " Undo"),
          m("input", {
            type: "search",
            value: "Search",
            id: "search",
            style: {
              color: "LightGrey",
              fontStyle: "italic",
              margin: "2px"
            }
          })
        ]),
        m("table", {
          class: "pure-table",
          style: {
            tableLayout: "fixed",
            width: "100%"
          }
        }, [
            m("thead", {
            id: "header",
            style: {
              display: "inline-block",
              width: "100%",
              overflow: "hidden"
            }
          }, [ header ]),
          m("tbody", {
            id: "rows",
            onscroll: vm.onscroll,
            style: {
              display: "inline-block",
              width: "100%",
              overflow: "auto"
            },
            config: tbodyConfig
          }, [rows])
        ]),
        m("div", {
            id: "tabs"
          }, tabs
        )
      ]);

      return view;
    };

    return component;
  };

}(f));

<commit_msg>Focus on clicked cell<commit_after>/**
    Framework for building object relational database apps

    Copyright (C) 2015  John Rogelstad
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

/*global window, f, m */
(function (f) {
  "use strict";

  var EDIT_MODE = 2,
    LIST_MODE = 1;

  // Calculate scroll bar width
  // http://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
  var scrWidth, inner, widthNoScroll, widthWithScroll,
    outer = document.createElement("div");

  outer.style.visibility = "hidden";
  outer.style.width = "100px";
  outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps

  document.body.appendChild(outer);

  widthNoScroll = outer.offsetWidth;
  // force scrollbars
  outer.style.overflow = "scroll";

  // add innerdiv
  inner = document.createElement("div");
  inner.style.width = "100%";
  outer.appendChild(inner);

  widthWithScroll = inner.offsetWidth;

  // remove divs
  outer.parentNode.removeChild(outer);
  scrWidth = widthNoScroll - widthWithScroll;

  // Define workbook view model
  f.viewModels.workbookViewModel = function (options) {
    var selection,
      sheet = options.sheet,
      frmroute = "/" + options.name + "/" + options.config[sheet].form.name,
      name = options.feather.toCamelCase(),
      vm = {};
    frmroute = frmroute.toSpinalCase();

    vm.config = function () {
      return options.config || {};
    };
    vm.models = f.models[name].list();
    vm.attrs = options.config[sheet].list.attrs || ["id"];
    vm.canSave = function (model) {
      var currentState = model.state.current()[0];
      return (currentState === "/Ready/New" ||
        currentState === "/Ready/Fetched/Dirty") &&
        model.isValid();
    };
    vm.goHome = function () {
      m.route("/home");
    };
    vm.goNextRow = function () {
      var list = vm.models(),
        model = vm.selection(),
        idx = list.indexOf(model) + 1;
      if (list.length > idx) {
        vm.select(list[idx]);
      }
    };
    vm.goPrevRow = function () {
      var list = vm.models(),
        model = vm.selection(),
        idx = list.indexOf(model) - 1;
      if (idx >= 0) {
        vm.select(list[idx]);
      }
    };
    vm.focusColumn = m.prop(vm.attrs[0]);
    vm.canSave = function () {
      return vm.models().some(function (model) {
        return model.canSave();
      });
    };
    vm.hasNoSelection = function () {
      return !selection;
    };
    vm.isSelected = function (model) {
      return selection === model;
    };
    vm.modelDelete = function () {
      // Delete now in list mode
      if (vm.mode() === LIST_MODE) {
        selection.delete(true).then(function () {
          vm.models().remove(selection);
        });
        return;
      }

      // Mork for deletion in edit mode
      selection.delete();
    };
    vm.mode = m.prop(LIST_MODE);
    vm.modelNew = function () {
      m.route(frmroute);
    };
    vm.modelOpen = function () {
      m.route(frmroute + "/" + selection.data.id());
    };
    vm.nextFocus = m.prop();
    vm.onkeydown = function (e) {
      var id,
        nav = function (name) {
          id = e.srcElement.id;
          // Navigate in desired direction
          m.startComputation();
          vm[name]();
          m.endComputation();
          // Set focus on the same cell we left
          m.startComputation();
          document.getElementById(id).focus();
          m.endComputation();
        };

      switch (e.key || e.keyIdentifier)
      {
      case "Up":
        nav("goPrevRow");
        break;
      case "Down":
        nav("goNextRow");
        break;
      }
    };
    vm.onscroll = function () {
      var rows = document.getElementById("rows"),
        header = document.getElementById("header");

      // Sync header position with table body position
      header.scrollLeft = rows.scrollLeft;
    };
    vm.scrollbarWidth = function () {
      return scrWidth;
    };
    vm.saveAll = function () {
      vm.models().forEach(function (model) {
        model.save().then(function() {
          if (model.state.current()[0] === "/Deleted") {
            vm.models().remove(model);
          }
        });
      });
    };
    vm.select = function (model) {
      if (selection !== model) {
        selection = model;
      }
      return selection;
    };
    vm.selection = function () {
      return selection;
    };
    vm.selectedTab = m.prop(options.sheet);
    vm.sheets = function () {
      return Object.keys(options.config || {});
    };
    vm.tabClicked = function (sheet) {
      var route = "/" + options.name + "/" + sheet;
      route = route.toSpinalCase();
      m.route(route);
    };
    vm.toggleMode = function () {
      var mode = vm.mode() === LIST_MODE ? EDIT_MODE : LIST_MODE;
      vm.mode(mode);
    };
    vm.toggleOpen = function (model) {
      selection = model;
      vm.modelOpen();
    };
    vm.toggleSelection = function (model, col) {
      var mode = vm.mode(),
        isSelected = selection === model;

      // Toggle row off
      if (isSelected && mode === LIST_MODE) {
        vm.select(undefined);
        return false;
      }

      // Select new row
      vm.select(model);

      // If editing, focus on clicked cell
      if (!isSelected && mode === EDIT_MODE) {
        vm.nextFocus("input" + col.toCamelCase(true));
      }
      return true;
    };
    vm.undo = function () {
      if (selection) { selection.undo(); }
    };

    return vm;
  };

  // Define workbook component
  f.components.workbookDisplay = function (options) {
    var component = {};

    component.controller = function () {
      this.vm = f.viewModels.workbookViewModel(options);
    };

    component.view = function (ctrl) {
      var tbodyConfig, header, rows, tabs, view,
        vm = ctrl.vm;

      // Define scrolling behavior for table body
      tbodyConfig = function (e) {
        var tb = document.getElementById("toolbar"),
          hd = document.getElementById("header"),
          ts = document.getElementById("tabs"),
          mh = window.innerHeight - tb.clientHeight - hd.clientHeight - ts.clientHeight- 12;

        // Set fields table to scroll and toolbar to stay put
        document.documentElement.style.overflow = 'hidden';
        e.style.height = mh + "px";

        // Key down handler for up down movement
        e.addEventListener('keydown', vm.onkeydown);
      };

      // Build header
      header = (function () {
        var ths = vm.attrs.map(function (key) {
            return m("th", {
              style: {
                minWidth: "100px",
                maxWidth: "100px",
                whiteSpace: "nowrap",
                overflow: "hidden",
                textOverflow: "ellipsis"
              }
            },
              key.toProperCase(true));
          });

        // Front cap header navigation
        ths.unshift(m("th", {style: {minWidth: "16px"}}));

        // End cap on header for scrollbar
        ths.push(m("th", {
          style: {
            minWidth: vm.scrollbarWidth() + "px",
            maxWidth: vm.scrollbarWidth() + "px"
          }
        }));
        return m("tr", ths);
      }());

      // Build rows
      rows = vm.models().map(function (model) {
        var tds, row, thContent,
          mode = vm.mode(),
          color = "White",
          isSelected = vm.isSelected(model),
          d = model.data,
          rowOpts = {};

        // Build view row
        if (mode === LIST_MODE || !isSelected) {
          // Build cells
          tds = vm.attrs.map(function (col) {
            var cell,
              value = d[col](),
              hasLocale = value !== null &&
                typeof value.toLocaleString === "function";

            // Build cell
            cell = m("td", {
                onclick: vm.toggleSelection.bind(this, model, col),
                style: {
                  minWidth: "100px",
                  maxWidth: "100px",
                  whiteSpace: "nowrap",
                  overflow: "hidden",
                  textOverflow: "ellipsis"
                }
              },
              hasLocale ? value.toLocaleString() : value);

            return cell;
          });

          // Build row
          if (isSelected) {
            color = "LightBlue";
          }

          rowOpts = {
            ondblclick: vm.toggleOpen.bind(this, model)
          };

        // Build editable row
        } else {
          // Build cells
          tds = vm.attrs.map(function (col) {
            var cell, cellOpts,
              id = "input" + col.toCamelCase(true);

            cellOpts = {
              id: id,
              onclick: vm.toggleSelection.bind(this, model, col),
              onchange: m.withAttr("value", d[col]),
              value: d[col](),
              config: function (e) {
                if (vm.nextFocus() === id) {
                  e.focus();
                  vm.nextFocus(undefined);
                }
              },
              style: {
                minWidth: "100px",
                maxWidth: "100px"
              }
            };

            // Build cell
            cell = m("td", [
              m("input", cellOpts)
            ]);

            return cell;
          });
        }

        // Front cap header navigation
        if (model.state.current()[0] === "/Delete") {
          thContent = [m("i", {class:"fa fa-remove"})];
        } else if (model.canSave()) {
          thContent = [m("i", {class:"fa fa-check"})];
        } else {
          thContent = {
            style: {
              minWidth: "16px"
            }
          };
        }
        tds.unshift(m("th", thContent));

        // Build row
        rowOpts.style = { backgroundColor: color };
        row = m("tr", rowOpts, tds);

        return row;
      });

      // Build tabs
      tabs = vm.sheets().map(function (sheet) {
        var tab,
          sconfig = vm.config()[sheet],
          label = sconfig.feather ?
            sheet : f.catalog.getFeather(sheet).plural;

        // Build tab
        tab = m("button[type=button]", {
          class: vm.selectedTab() === sheet ?
            "pure-button pure-button-active" : "pure-button",
          style: {
            borderTopLeftRadius: "0px",
            borderTopRightRadius: "0px"
          },
          onclick: vm.tabClicked.bind(this, sheet)
        }, label);

        return tab;
      });

      // Finally assemble the whole view
      view = m("form", {
        class: "pure-form"
      }, [
        m("div", {
            id: "toolbar"
          }, [
          m("button", {
            type: "button",
            class: "pure-button",
            style: { margin: "1px" },
            onclick: vm.goHome
          }, [m("i", {class:"fa fa-home"})], " Home"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === EDIT_MODE ? "none" : "inline-block"
            },
            onclick: vm.toggleMode
          }, [m("i", {class:"fa fa-pencil"})], " Edit"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === LIST_MODE ? "none" : "inline-block"
            },
            onclick: vm.toggleMode
          }, [m("i", {class:"fa fa-th-list"})], " List"),
            m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === LIST_MODE ? "none" : "inline-block"
            },
            onclick: vm.saveAll,
            disabled: !vm.canSave()
          }, [m("i", {class:"fa fa-save"})], " Save"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: vm.mode() === EDIT_MODE ? "none" : "inline-block"
            },
            onclick: vm.modelOpen,
            disabled: vm.hasNoSelection()
          }, [m("i", {class:"fa fa-folder-open"})], " Open"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: { margin: "1px"},
            onclick: vm.modelNew
          }, [m("i", {class:"fa fa-plus-circle"})], " New"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: (vm.selection() && vm.selection().canSave()) ?
                "none" : "inline-block"
            },
            onclick: vm.modelDelete,
            disabled: vm.hasNoSelection()
          }, [m("i", {class:"fa fa-remove"})], " Delete"),
          m("button", {
            type: "button",
            class: "pure-button",
            style: {
              margin: "1px",
              display: (vm.selection() && vm.selection().canSave()) ?
                "inline-block" : "none"
            },
            onclick: vm.undo
          }, [m("i", {class:"fa fa-undo"})], " Undo"),
          m("input", {
            type: "search",
            value: "Search",
            id: "search",
            style: {
              color: "LightGrey",
              fontStyle: "italic",
              margin: "2px"
            }
          })
        ]),
        m("table", {
          class: "pure-table",
          style: {
            tableLayout: "fixed",
            width: "100%"
          }
        }, [
            m("thead", {
            id: "header",
            style: {
              display: "inline-block",
              width: "100%",
              overflow: "hidden"
            }
          }, [ header ]),
          m("tbody", {
            id: "rows",
            onscroll: vm.onscroll,
            style: {
              display: "inline-block",
              width: "100%",
              overflow: "auto"
            },
            config: tbodyConfig
          }, [rows])
        ]),
        m("div", {
            id: "tabs"
          }, tabs
        )
      ]);

      return view;
    };

    return component;
  };

}(f));

<|endoftext|>


<commit_before>define(['logManager', 'js/NodePropertyNames'], function (logManager, nodePropertyNames) {
  "use strict";

  var logger, client;

  var ObjectConstraintManager = function(client_) {
    logger = logManager.create('ObjectConstraintManager');
    client = client_;
  };

  ObjectConstraintManager.prototype.validateAll = function(id) {

    validate(id);

    function validate (id) {

      // Validate the node itself..
      logger.info("Validating node: " + id);
      var node = client.getNode(id);
      var constraint = node.getAttribute(nodePropertyNames.Attributes.OCLConstraint);
      if ( constraint !== undefined && constraint !== "" ) {
        var result = eval("(" + constraint + ")();");
        if (result) {
            logger.warn('No Validation');
        } else {
            logger.warn('!!Validation!!!');
        }
      }

      // Call validation for the node's children
      var children = node.getChildrenIds();
      children.forEach(function(child) {
        validate(child);
      });

    }

  };

  return ObjectConstraintManager;
});<commit_msg>Constraints object logic is introduced to define constraints in it To define a constraint or constraints for a model, a child object called 'Constraints' should be created in the model itself. Within the 'Constraints' object one can define as many costraints as he wants by creating elements and defining functions in the input area. Name of a constraint is used to display the statuses of validations.<commit_after>define(['logManager', 'js/NodePropertyNames'], function (logManager, nodePropertyNames) {
  "use strict";

  var logger, client;

  var ObjectConstraintManager = function(client_) {
    logger = logManager.create('ObjectConstraintManager');
    client = client_;
  };

  ObjectConstraintManager.prototype.validateAll = function(id) {

    validate(id);

    function validate (id) {

      // Validate the node itself..
      logger.info("Validating node: " + id);
      var node = client.getNode(id);

      // find if the node has a Constraint Object
      // and collect them in a constraints array
      var constraints = [];

      var children = node.getChildrenIds();
      children.forEach(function(child) {
        var child_node = client.getNode(child);
        var child_name = child_node.getAttribute('name');
        if (child_name == 'Constraints') {
          var grandchildren = child_node.getChildrenIds();
          grandchildren.forEach(function(grandchild) {
            var grandchild_node = client.getNode(grandchild);
            var grandchild_name = grandchild_node.getAttribute('name');
            var constraint = grandchild_node.getAttribute(nodePropertyNames.Attributes.OCLConstraint);
            if ( constraint !== undefined || constraint !== "" ) {
              var constraint_obj = {
                name: grandchild_name,
                constraint: constraint
              };
              constraints.push(constraint_obj);
            }
          });
        }
      });

      constraints.forEach(function(constraint) {

        var result = eval("(" + constraint.constraint + ")();");

        var msg = "[[ " + constraint.name + " ]]";
        if (result) {
            logger.warn('No violation of a constraint on ' +
              msg);
        } else {
            logger.warn('!!Violation of a constraint on ' +
              msg + "!!!");
        }

      });

      // Call validation for the node's children
      var children = node.getChildrenIds();
      children.forEach(function(child) {
        validate(child);
      });

    }

  };

  return ObjectConstraintManager;
});<|endoftext|>


<commit_before>// Socket.io singleton
//
// Usage:
//   var socket = require('./socket');
//   socket.on('myevent', fn)

var socket = null;
var setupMessages = require('./messages');

var io = require('socket.io-client');
socket = io('/');

console.log('socket = io(/) called');
setupMessages(socket);

exports.on = function (eventName, handler) {
  socket.on(eventName, handler);
};

// An attempt to dynamically load socket.io-client. Everything
// seemed to work, queue seemed to work. Somehow it still did not work.
// Events were sent from server successfully. No problem.
// Yet, none of the events were received on the client.
// Back to bloat-bundle, yak.
//
// Maybe server needs some something immediate. I dont know.
//

// import(
//   /* webpackChunkName: "socket-io" */
//   'socket.io-client'
// )
//   .then(function (moduleWrap) {
//     var io = moduleWrap.default;
//     socket = io('/');
//
//     console.log('socket = io(/) called');
//     setupMessages(socket);
//
//     // Execute queued
//     Object.keys(queue).forEach(function (eventName) {
//       console.log('queue', eventName);
//       socket.on(eventName, queue[eventName]);
//     });
//     // Clear
//     queue = {};
//   })
//   .catch(function (err) {
//     console.log('An error occurred when loading socket.io-client');
//     console.error(err);
//   });

// exports.on = function (eventName, handler) {
//   console.log('on. if socket');
//   if (socket) {
//     console.log('socket set, socket.on', eventName);
//     socket.on(eventName, handler);
//   } else {
//     // Execute when loaded
//     queue[eventName] = handler;
//   }
// };
<commit_msg>Keep it simple<commit_after>// Socket.io singleton
//
// Usage:
//   var socket = require('./socket');
//   socket.on('myevent', fn)

var setupMessages = require('./messages');
var io = require('socket.io-client');

var socket = io('/');
setupMessages(socket);

exports.on = function (eventName, handler) {
  socket.on(eventName, handler);
};

// An attempt to dynamically load socket.io-client. Everything
// seemed to work, queue seemed to work. Somehow it still did not work.
// Events were sent from server successfully. No problem.
// Yet, none of the events were received on the client.
// Back to bloat-bundle, yak.
//
// Maybe server needs some something immediate. I dont know.
//

// import(
//   /* webpackChunkName: "socket-io" */
//   'socket.io-client'
// )
//   .then(function (moduleWrap) {
//     var io = moduleWrap.default;
//     socket = io('/');
//
//     console.log('socket = io(/) called');
//     setupMessages(socket);
//
//     // Execute queued
//     Object.keys(queue).forEach(function (eventName) {
//       console.log('queue', eventName);
//       socket.on(eventName, queue[eventName]);
//     });
//     // Clear
//     queue = {};
//   })
//   .catch(function (err) {
//     console.log('An error occurred when loading socket.io-client');
//     console.error(err);
//   });

// exports.on = function (eventName, handler) {
//   console.log('on. if socket');
//   if (socket) {
//     console.log('socket set, socket.on', eventName);
//     socket.on(eventName, handler);
//   } else {
//     // Execute when loaded
//     queue[eventName] = handler;
//   }
// };
<|endoftext|>


<commit_before>define([
    'modules/dc/views/dc',
    'modules/types/gen/dc/datplot',
    'modules/types/gen/dc/imagestatusitem',
    'modules/dc/views/dccomments',
    'tpl!templates/types/gen/dc/dc.html'], function(DCItemView, DatPlot, DCImageStatusItem, DCCommentsView, template) {

    return DCItemView.extend({
        template: template,
        plotView: DatPlot,
        imageStatusItem: DCImageStatusItem,

        events: {
            'click .distl': 'showPlot',
            'click .diffraction': 'showDiff',
            'click .atp': 'addToProject',
            'click .flag': 'flag',
            'click .comments': 'showComments',
            'click a.dl': 'showPlot',
            'click a.sn': 'showSnapshots',
        },

        showPlot: function(e) {
            e.preventDefault()
            app.dialog.show(new DialogView({ title: 'Dat Plot', view: new DatPlot({ parent: this.model }), autoSize: true }))
        },

        showDiff: function(e) {
            e.preventDefault()
            this.$el.find('.diffraction a').eq(0).trigger('click')
        },

    })

})<commit_msg>add sample -> dc associator<commit_after>define([
    'modules/dc/views/dc',
    'modules/types/gen/dc/datplot',
    'modules/types/gen/dc/assoc',
    'modules/types/gen/dc/imagestatusitem',
    'modules/dc/views/dccomments',
    'tpl!templates/types/gen/dc/dc.html'], function(DCItemView, DatPlot, AssocSampleView, DCImageStatusItem, DCCommentsView, template) {

    return DCItemView.extend({
        template: template,
        plotView: DatPlot,
        imageStatusItem: DCImageStatusItem,

        events: {
            'click .distl': 'showPlot',
            'click .diffraction': 'showDiff',
            'click .atp': 'addToProject',
            'click .flag': 'flag',
            'click .comments': 'showComments',
            'click a.dl': 'showPlot',
            'click a.sn': 'showSnapshots',
            'click a.assoc': 'associateSample',
        },

        associateSample: function(e) {
            e.preventDefault()
            app.dialog.show(new AssocSampleView({ model: this.model }))
            this.listenTo(app.dialog.currentView, 'sample:associated', this.updateSample, this)
        },

        updateSample: function() {
            var s = this.$el.find('.sample')
            console.log(s.length, this.$el.find('ul'))
            if (s.length) {
                var a = s.eq(0).find('a')
                a.html(this.model.get('SAMPLE')).attr('href', '/samples/sid/'+this.model.get('BLSAMPLEID'))

            } else {
                this.$el.find('ul').prepend('
Sample: '+this.model.get('SAMPLE')+'
')
            }

        },

        showPlot: function(e) {
            e.preventDefault()
            app.dialog.show(new DialogView({ title: 'Dat Plot', view: new DatPlot({ parent: this.model }), autoSize: true }))
        },

        showDiff: function(e) {
            e.preventDefault()
            this.$el.find('.diffraction a').eq(0).trigger('click')
        },

    })

})<|endoftext|>


<commit_before>"use strict";

angular.module("hikeio").
	factory("search", ["$http", "$log", "$q", "$rootScope", "navigation", "persistentStorage", "resourceCache", function($http, $log, $q, $rootScope, navigation, persistentStorage, resourceCache) {

		var SearchService = function() {
		};

		var getBestGeocodeResult = function(data) {
			// Loop over results until we find a city, state, province, country, etc.
			// results sholud be ordered by relevance
			if (data.status === "OK" && data.results && data.results.length > 0) {
				var results = data.results;
				for (var i = 0; i < results.length; i++) {
					var result = results[i];
					var types = result.types;
					for (var j = 0; j < types.length; j++) {
						// https://developers.google.com/maps/documentation/geocoding/#Types
						var type = types[j];
						if (type === "political" ||
							type === "country" ||
							type === "administrative_area_level_1" ||
							type === "administrative_area_level_2" ||
							type === "administrative_area_level_3" ||
							type === "locality" ||
							type === "sublocality" ||
							type === "sublocality_level_1" ||
							type === "postal_code" ||
							type === "park" ||
							type === "natural_feature") {
							return result;
						}
					}
				}
			}
		};

		var getMapViewportFromGeocodeResult = function(result) {
			var viewport = result.geometry.viewport;
			return {
				northEast: {
					latitude: viewport.northeast.lat,
					longitude: viewport.northeast.lng
				},
				southWest: {
					latitude: viewport.southwest.lat,
					longitude: viewport.southwest.lng
				}
			};
		};

		var cleanupFormattedAddress = function(address) {
			// Remove zip code
			var result = address.replace(/ \d{5}/, "");

			// Remove USA, unless there is another locality in the string. We want it to say Washington, USA. But not Seattle, WA, USA.
			if (result.split(",").length !== 2) {
				result = result.replace(/, USA/, "");
			}
			return result;
		};

		var searchByLocation = function(query) {
			// This request should be automatically biased based on where the request is coming from
			return $http({method: "GET", url: "http://maps.googleapis.com/maps/api/geocode/json", params: { address: query, sensor: false }, cache: resourceCache}).
				success(function(data) {
					// First make sure that what we got back is actually useful, we don't want to bring up the map to a specific address.
					var result = getBestGeocodeResult(data);
					if (result) {
						var viewport = getMapViewportFromGeocodeResult(result);
						persistentStorage.set("/map", { viewport: viewport, formattedLocationString: cleanupFormattedAddress(result.formatted_address)});
						if (navigation.onMap()) {
							$rootScope.$broadcast("resetMapViewport", viewport);
						} else {
							navigation.toMap();
						}
					}
				}).error(function(data, status, headers, config) {
					$log.error(data, status, headers, config);
				});
		};

		var searchByName = function(query) {
			return $http({method: "GET", url: "/api/v1/hikes/search", params: { q: query }, cache: resourceCache}).
				success(function(data, status, headers, config) {
					var relevanceThreshold = 0.7;
					if (data.length === 1 && data[0].relevance > relevanceThreshold) {
						var hike = data[0].hike;
						resourceCache.put("/api/v1/hikes/" + hike.string_id, jQuery.extend(true, {}, hike));
						navigation.toEntry(hike.string_id);
					} else {
						// If any of the results are of high enough relevance, then we want to see those results first
						// If they're low quality matches, try searching by location.
						for (var i = 0; i < data.length; i++) {
							var result = data[i];
							if (result.relevance > relevanceThreshold) {
								navigation.toSearch(query);
								break;
							}
						}
					}
				}).
				error(function(data, status, headers, config) {
					$log.error(data, status, headers, config);
				});
		};

		SearchService.prototype.search = function(query) {
			var deferred = $q.defer();
			var promise = deferred.promise;

			// Secret feature, allow user to force searching by location if they prepend their query with a !
			if (query[0] === "!") {
				searchByLocation(query.slice(1)).then(function() {
					deferred.resolve();
				});
				return promise;
			}

			// Otherwise, first check to see if there is a hike with this name
			searchByName(query).then(function(result) {
				if (result.data.length !== 0) {
					// Unable to find good match name, try by location
					searchByLocation(query).then(function(result) {
						if (!getBestGeocodeResult(result.data)) {
							navigation.toSearch(query);
						}
						deferred.resolve();
					});
				} else {
					deferred.resolve();
				}
			});
			return promise;
		};

		return new SearchService();
	}]);
<commit_msg>Only search by location if there are not any highly relevant results when searching by name.<commit_after>"use strict";

angular.module("hikeio").
	factory("search", ["$http", "$log", "$q", "$rootScope", "navigation", "persistentStorage", "resourceCache", function($http, $log, $q, $rootScope, navigation, persistentStorage, resourceCache) {

		var SearchService = function() {
		};

		var getBestGeocodeResult = function(data) {
			// Loop over results until we find a city, state, province, country, etc.
			// results sholud be ordered by relevance
			if (data.status === "OK" && data.results && data.results.length > 0) {
				var results = data.results;
				for (var i = 0; i < results.length; i++) {
					var result = results[i];
					var types = result.types;
					for (var j = 0; j < types.length; j++) {
						// https://developers.google.com/maps/documentation/geocoding/#Types
						var type = types[j];
						if (type === "political" ||
							type === "country" ||
							type === "administrative_area_level_1" ||
							type === "administrative_area_level_2" ||
							type === "administrative_area_level_3" ||
							type === "locality" ||
							type === "sublocality" ||
							type === "sublocality_level_1" ||
							type === "postal_code" ||
							type === "park" ||
							type === "natural_feature") {
							return result;
						}
					}
				}
			}
		};

		var getMapViewportFromGeocodeResult = function(result) {
			var viewport = result.geometry.viewport;
			return {
				northEast: {
					latitude: viewport.northeast.lat,
					longitude: viewport.northeast.lng
				},
				southWest: {
					latitude: viewport.southwest.lat,
					longitude: viewport.southwest.lng
				}
			};
		};

		var cleanupFormattedAddress = function(address) {
			// Remove zip code
			var result = address.replace(/ \d{5}/, "");

			// Remove USA, unless there is another locality in the string. We want it to say Washington, USA. But not Seattle, WA, USA.
			if (result.split(",").length !== 2) {
				result = result.replace(/, USA/, "");
			}
			return result;
		};

		var searchByLocation = function(query) {
			// This request should be automatically biased based on where the request is coming from
			return $http({method: "GET", url: "http://maps.googleapis.com/maps/api/geocode/json", params: { address: query, sensor: false }, cache: resourceCache}).
				success(function(data) {
					// First make sure that what we got back is actually useful, we don't want to bring up the map to a specific address.
					var result = getBestGeocodeResult(data);
					if (result) {
						var viewport = getMapViewportFromGeocodeResult(result);
						persistentStorage.set("/map", { viewport: viewport, formattedLocationString: cleanupFormattedAddress(result.formatted_address)});
						if (navigation.onMap()) {
							$rootScope.$broadcast("resetMapViewport", viewport);
						} else {
							navigation.toMap();
						}
					}
				}).error(function(data, status, headers, config) {
					$log.error(data, status, headers, config);
				});
		};

		var hasRelevantSearchResults = function(searchData) {
			for (var i = 0; i < searchData.length; i++) {
				var result = searchData[i];
				if (result.relevance > relevanceThreshold) {
					return true;
				}
			}
			return false;
		};

		var searchByName = function(query) {
			return $http({method: "GET", url: "/api/v1/hikes/search", params: { q: query }, cache: resourceCache}).
				success(function(data, status, headers, config) {
					var relevanceThreshold = 0.7;
					if (data.length === 1 && data[0].relevance > relevanceThreshold) {
						var hike = data[0].hike;
						resourceCache.put("/api/v1/hikes/" + hike.string_id, jQuery.extend(true, {}, hike));
						navigation.toEntry(hike.string_id);
					} else {
						// If any of the results are of high enough relevance, then we want to see those results first
						// If they're low quality matches, try searching by location.
						if (hasRelevantSearchResults(data)){
							navigation.toSearch(query);
						}
					}
				}).
				error(function(data, status, headers, config) {
					$log.error(data, status, headers, config);
				});
		};

		SearchService.prototype.search = function(query) {
			var deferred = $q.defer();
			var promise = deferred.promise;

			// Secret feature, allow user to force searching by location if they prepend their query with a !
			if (query[0] === "!") {
				searchByLocation(query.slice(1)).then(function() {
					deferred.resolve();
				});
				return promise;
			}

			// Otherwise, first check to see if there is a hike with this name
			searchByName(query).then(function(result) {
				if (!hasRelevantSearchResults(result.data)) {
					// Unable to find good match name, try by location
					searchByLocation(query).then(function(result) {
						if (!getBestGeocodeResult(result.data)) {
							navigation.toSearch(query);
						}
						deferred.resolve();
					});
				} else {
					deferred.resolve();
				}
			});
			return promise;
		};

		return new SearchService();
	}]);
<|endoftext|>


<commit_before>/**
* editableText plugin that uses contentEditable property
* Copyright (c) 2009 Andris Valums, http://valums.com
* Licensed under the MIT license (http://valums.com/mit-license/)
*/
(function(){
  // The dollar sign could be overwritten globally, but jQuery should always stay accesible
  var $ = jQuery;

  // Usage $('selector).editableText(optionArray);
  $.fn.editableText = function(){

    // Show toolbar
    function showToolbar(element) {
      if ($(element).is('.editableToolbar')) {
        $(element).get(0).target.entity.hover();
        return true;
      } else {
        var toolbar = $('.editableToolbar'),
            curTarget = toolbar.get(0).target,
            newTarget = element;
        if ($(curTarget).attr('contentEditable') === 'true') {
          return;
        }
        if (curTarget && curTarget !== newTarget) {
          hideToolbar(curTarget);
        }
        var left = newTarget.getBoundingClientRect().left + window.scrollX,
            top = newTarget.getBoundingClientRect().top + window.scrollY;
        toolbar.css('left', left + 'px')
               .css('top', top-21 + 'px');
      }
      var toolbarNode = toolbar.get(0);
      if (toolbarNode.I !== null) {
        clearTimeout(toolbarNode.I);
        toolbarNode.I = null;
      }
      if (newTarget) {
        toolbarNode.target = newTarget;
      }
      $(newTarget).addClass('hovered');
      toolbar.show();
    }

    // Hide toolbar
    function hideToolbar(element) {
      if ($(element).is('.editableToolbar')) {
        var toolbar = $(element);
      } else {
        var toolbar = $('.editableToolbar');
      }
      var toolbarNode = toolbar.get(0),
          target = toolbarNode.target;
      if ($(target).attr('contentEditable') === 'true') {
        return;
      }
      function hide() {
        if (target) {
          target.blur();
          stopEditing(toolbar);
          if (target === toolbar.get(0).target) {
            toolbar.get(0).target = null;
            $(target).removeClass('hovered');
            toolbar.hide();
          } else {
            $(target).removeClass('hovered');
          }
        }
      }
      toolbar.get(0).I = setTimeout(hide, 50);
    }

    // Makes element editable
    function startEditing(toolbar) {
      toolbar.children().show().end()
        .find('.edit').hide();
      var target = toolbar.get(0).target;
      $(target).attr('contentEditable', true);
      $(target.entity.ui).addClass("active");
      target.focus();
    }

    // Makes element non-editable
    function stopEditing(toolbar) {
      toolbar.children().hide().end()
        .find('.edit').show();
      var target = toolbar.get(0).target;
      $(target).attr('contentEditable', false);
      $(target.entity.ui).removeClass("active");
    }

    if (!$('.editableToolbar').length) {
      // Define toolbar
      var toolbar = $(
        "
" +
          "" +
          "" +
          "" +
        "
").appendTo($('body'));

      // Attach events
      toolbar.hover(function () {
        showToolbar(this);
      }, function () {
        this.target.entity.unhover();
      })
      .find('.edit').click(function () {
        startEditing(toolbar);
        return false;
      }).end()
      .find('.save, .cancel').click(function () {
        stopEditing(toolbar);
        return false;
      });
    }

    return this.each(function () {
      // Save value to restore if user presses cancel
      this.prevValue = $(this).html().toString();

      // Show/hide toolbar
      this.showToolbar = function () {
        showToolbar(this);
      }
      this.hideToolbar = function () {
        hideToolbar(this);
      }

      // Hover handler
      $(this).hover(function () {
        this.entity.hover();
      }, function() {
        this.entity.unhover();
      });
    });
  }
})();<commit_msg>jquery.editableText.js is dead<commit_after><|endoftext|>


<commit_before>let POST_WIDTH = 0.75;
let POST_HEIGHT = 0.875;

Template.mainDetailedPost.getParents();

Template.mainDetailedPost.onCreated(function() {
    let instance = this;
    let onSubReady = new Notifier();
    this.onRendered = new Notifier();

    let user = Meteor.users.findOne({_id: Meteor.userId()});

    if (postIsSeen(instance.data))
        instance.seen = true;

    this.subscribe('post', this.data._id, this.data.poster, {onReady: onSubReady.fulfill});

    Notifier.all(onSubReady, this.onRendered).onFulfilled(function() {
        //Fade out spinner and fade in actual post.
        instance.div.children('.main-detailed-post-spinner').fadeOut(100);
        //instance.div.children('.main-detailed-post-info').css('overflow', 'visible');
        instance.div.children('.main-detailed-post-flex')
            .css('display', 'flex')
            .hide()
            .fadeIn(200);

        if (instance.data.postedOn && instance.data.poster != Meteor.userId() && Date.now() - instance.data.postedOn < (1000*60*60*24*30) && !instance.seen) {
            instance.div.addClass('unseen');
            Meteor.call('addSeenPost', instance.data._id);
        }
    });
});

Template.mainDetailedPost.onRendered(function() {
    this.div = $('#main-detailed-post-' + this.data._id);
    this.div.css('display', 'flex').hide().fadeIn(200);
    setTimeout(this.onRendered.fulfill, 250);
});

Template.mainDetailedPost.helpers({
    poster: function() {
        let post = Template.currentData();
        return Meteor.users.findOne({_id: post.poster});
    },
    age: function() {
        let post = Template.currentData();
        if (post.postedOn) {
            return new Date(post.postedOn).toDateString();
        }
    },
    currentUser: function() {
        return (Meteor.user());
    },
    verifiedUser:function() {
        let user = Meteor.user();
        return (user.emails && user.emails.length > 0 && user.emails[0].verified);
    },
    content: function() {
        let rawContent = Template.currentData().content;
        if (rawContent) {
            return XBBCODE.process({
                text: rawContent,
                removeMisalignedTags: false,
                addInLineBreaks: true
            }).html;
        }
    },
    editAccess: function() {
        return this.poster === Meteor.userId() || Roles.userIsInRole(Meteor.userId(), ['moderator']);
    },
    moderator: function() {
        return Roles.userIsInRole(Meteor.userId(), ['moderator']);
    },
    hasReplyButtons: function() {
        return !Template.instance().parent.isReplyBoxOpen();
    },
    hasReportButton: function() {
        return Template.instance().parent.reportTarget.get() === undefined;
    },
    seen: function() {
        return (!this.postedOn || this.poster == Meteor.userId() || Date.now() - this.postedOn >= (1000*60*60*24*30) || Template.instance().seen);
    }
});

Template.mainDetailedPost.events({
    'click .resend-verification-link': function( event, template ) {
        Meteor.call( 'sendVerificationLink', ( error, response ) => {
        if ( error ) {
            window.alert( error.reason, 'danger' );
        } else {
            let email = Meteor.user().emails[ 0 ].address;
            window.alert( `Verification sent to ${ email }!`, 'success' );
        }
        });
    },
    'click .main-detailed-post-close-button': function() {
        Template.instance().parent.removePost(this);
    },
    'mousedown, touchstart, mousemove, touchmove, mouseup, touchend, wheel': function(event, instance) {
        if (instance.parent.camera.isDragging()) {
            //Prevents interaction while dragging.
            event.preventDefault();
        }
        else {
            //Prevent events from passing through posts into the WebGL canvas.
            event.stopImmediatePropagation();
        }
    }
});

MainViewDetailedPosts = function(camera, partitioner, localPostPositions) {
    let self = this;

    //Collection of currently visible detailed posts.
    let visiblePosts = new Mongo.Collection(null);
    // Sort by position, left to right and then top to bottom
    let visiblePostsCursor = visiblePosts.find({}, {sort: {position: 1}});
    this.showFullPosts = new ReactiveVar(false);

    this.init = function(postArray) {
    };

    this.addPost = function(post) {

    };

    this.removePost = function(post) {
        let div;

        if (self.showFullPosts.get()) {
            div = $('#main-detailed-post-' + post._id);
        } else {
            div = $('#main-basic-post-' + post._id);
        }

        div.fadeOut(200, function() {
            visiblePosts.remove({_id: post._id});
        });
    };

    this.updatePost = function(id, fields) {
        if (visiblePosts.findOne({_id: id}))
            visiblePosts.update({_id: id}, {$set: fields});
    };

    this.update = function() {
        //switch between showing basic posts and detailed posts.
        if (self.showFullPosts.get()) {
            if (camera.getScale() <= 160) {
                let div = $('.main-detailed-post');

                div.fadeOut(200, function() {
                    self.showFullPosts.set(false);
                });
            }
        } else {
            if (camera.getScale() > 160) {
                let div = $('.main-basic-post');

                div.fadeOut(200, function() {
                    self.showFullPosts.set(true);
                });
            }
        }
        visiblePostsCursor.forEach(function(post) {
            if (!camera.isPointVisible(post.position) ||
                ((2 + post.replies.length) <= 5 * (1-camera.getZoomFraction()))) {
                self.removePost(post);
            }
        });

        //Add posts which are newly visible.
        let visible = partitioner.getVisible();
        for (let post of visible) {
            if (!visiblePosts.findOne({_id: post._id}) &&
                ((2 + post.replies.length) > 5 * (1-camera.getZoomFraction()))) {

                if (!post.replies || post.replies == undefined) post.replies = [];
                visiblePosts.insert(post);
            }
        }

        //Update post positions/sizes.
        var postMemo = new Map(); // Keep track of the position of each post
        visiblePostsCursor.forEach(function(post) {
            let div;

            if (self.showFullPosts.get()) {
                div = $('#main-detailed-post-' + post._id);
                div.width(POST_WIDTH*camera.getScale());
                div.css('max-height', POST_HEIGHT*camera.getScale());
            } else {
                div = $('#main-basic-post-' + post._id);
            }

            let pos = camera.toScreen(post.position);
            let pleft = pos.x - div.outerWidth()/2;
            let ptop = pos.y - div.outerHeight()/2;
            postMemo.forEach(function(tgtPosition, tgtPost) {
              let tgtx = tgtPosition[0];
              let tgty = tgtPosition[1];
              if (pleft <= tgtx && ptop <= tgty) {
                pleft += (tgtx - pleft) + 10;
                ptop += (tgty - ptop) + 10;
              }
            });
            div.css('left', pleft);
            div.css('top', ptop);
            postMemo.set(post._id, [pleft + div.outerWidth(), ptop + div.outerHeight()]);
        });
    };

    this.find = function() {
        return visiblePostsCursor;
    };
};

Template.mainDetailedPostReplyButton.getParents();

Template.mainDetailedPostReplyButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        instance.parent.parent.replyTarget.set(instance.parent.data);
    }
});

Template.mainDetailedPostEditButton.getParents();

Template.mainDetailedPostEditButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        instance.parent.parent.editTarget.set(instance.parent.data);
    }
});

Template.mainDetailedPostReportButton.getParents();

Template.mainDetailedPostReportButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        instance.parent.parent.reportTarget.set(instance.parent.data);
    }
});

Template.mainDetailedPostDeleteButton.getParents();

Template.mainDetailedPostDeleteButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        if (confirm("Are you sure you want to delete this post?")) {
            Meteor.call('deletePost', instance.parent.data._id);
        }
    }
});

Template.mainBasicPost.getParents();

Template.mainBasicPost.onCreated(function() {
    let instance = this;
    let onSubReady = new Notifier();
    this.onRendered = new Notifier();

    let user = Meteor.users.findOne({_id: Meteor.userId()});

    if (postIsSeen(instance.data))
        instance.seen = true;

    this.subscribe('post', this.data._id, this.data.poster, {onReady: onSubReady.fulfill});

    Notifier.all(onSubReady, this.onRendered).onFulfilled(function() {
        //Fade out spinner and fade in actual post.
        instance.div.children('.main-basic-post-spinner').fadeOut(100);
        instance.div.css('overflow', 'visible');
        instance.div.children('.main-basic-post-flex')
            .css('display', 'flex')
            .hide()
            .fadeIn(200);

        if (instance.data.postedOn && instance.data.poster != Meteor.userId() && Date.now() - instance.data.postedOn < (1000*60*60*24*30) && !instance.seen)
            instance.div.addClass('unseen');
    });
});

Template.mainBasicPost.onRendered(function() {
    this.div = $('#main-basic-post-' + this.data._id);
    this.div.css('display', 'flex').hide().fadeIn(200);
    setTimeout(this.onRendered.fulfill, 250);
});

Template.mainBasicPost.helpers({
    poster: function() {
        let post = Template.currentData();
        return Meteor.users.findOne({_id: post.poster});
    },
    preview: function() {
        if (Template.currentData().title) return Template.currentData().title.slice(0, 20);
        else {
            let rawContent = Template.currentData().content;
            let bbcontent, finalContent = "";
            if (rawContent) {
                bbcontent = XBBCODE.process({
                    text: rawContent,
                    removeMisalignedTags: false,
                    addInLineBreaks: true
                }).html;

                let insideTags = 0, openTags = 0, characters = 20

                while (bbcontent.length > 0 || insideTags > 0 || openTags > 0) {

                    if (bbcontent[0] == '<') {
                        insideTags++;
                        if (bbcontent[1] != '/')
                            openTags++;
                        else openTags--;
                    }

                    if (characters > 0 && insideTags < 1) {
                        if (bbcontent[0] != '\n') finalContent = finalContent + bbcontent[0];
                        else finalContent = finalContent + ' ';
                    }

                    if (insideTags <= 0) characters--;

                    if (bbcontent[0] == '>') insideTags--;

                    bbcontent = bbcontent.substr(1, bbcontent.length - 1);

                }

                return finalContent;

            }
        }
    },
    seen: function() {
        return (!this.postedOn || this.poster == Meteor.userId() || Date.now() - this.postedOn >= (1000*60*60*24*30) || Template.instance().seen);
    }
});
<commit_msg>Instead of shuffling overlapping posts, simply don’t display the offending post. The post displays as soon as a redraw is requested where the post no longer overlaps.<commit_after>let POST_WIDTH = 0.75;
let POST_HEIGHT = 0.875;

Template.mainDetailedPost.getParents();

Template.mainDetailedPost.onCreated(function() {
    let instance = this;
    let onSubReady = new Notifier();
    this.onRendered = new Notifier();

    let user = Meteor.users.findOne({_id: Meteor.userId()});

    if (postIsSeen(instance.data))
        instance.seen = true;

    this.subscribe('post', this.data._id, this.data.poster, {onReady: onSubReady.fulfill});

    Notifier.all(onSubReady, this.onRendered).onFulfilled(function() {
        //Fade out spinner and fade in actual post.
        instance.div.children('.main-detailed-post-spinner').fadeOut(100);
        //instance.div.children('.main-detailed-post-info').css('overflow', 'visible');
        instance.div.children('.main-detailed-post-flex')
            .css('display', 'flex')
            .hide()
            .fadeIn(200);

        if (instance.data.postedOn && instance.data.poster != Meteor.userId() && Date.now() - instance.data.postedOn < (1000*60*60*24*30) && !instance.seen) {
            instance.div.addClass('unseen');
            Meteor.call('addSeenPost', instance.data._id);
        }
    });
});

Template.mainDetailedPost.onRendered(function() {
    this.div = $('#main-detailed-post-' + this.data._id);
    this.div.css('display', 'flex').hide().fadeIn(200);
    setTimeout(this.onRendered.fulfill, 250);
});

Template.mainDetailedPost.helpers({
    poster: function() {
        let post = Template.currentData();
        return Meteor.users.findOne({_id: post.poster});
    },
    age: function() {
        let post = Template.currentData();
        if (post.postedOn) {
            return new Date(post.postedOn).toDateString();
        }
    },
    currentUser: function() {
        return (Meteor.user());
    },
    verifiedUser:function() {
        let user = Meteor.user();
        return (user.emails && user.emails.length > 0 && user.emails[0].verified);
    },
    content: function() {
        let rawContent = Template.currentData().content;
        if (rawContent) {
            return XBBCODE.process({
                text: rawContent,
                removeMisalignedTags: false,
                addInLineBreaks: true
            }).html;
        }
    },
    editAccess: function() {
        return this.poster === Meteor.userId() || Roles.userIsInRole(Meteor.userId(), ['moderator']);
    },
    moderator: function() {
        return Roles.userIsInRole(Meteor.userId(), ['moderator']);
    },
    hasReplyButtons: function() {
        return !Template.instance().parent.isReplyBoxOpen();
    },
    hasReportButton: function() {
        return Template.instance().parent.reportTarget.get() === undefined;
    },
    seen: function() {
        return (!this.postedOn || this.poster == Meteor.userId() || Date.now() - this.postedOn >= (1000*60*60*24*30) || Template.instance().seen);
    }
});

Template.mainDetailedPost.events({
    'click .resend-verification-link': function( event, template ) {
        Meteor.call( 'sendVerificationLink', ( error, response ) => {
        if ( error ) {
            window.alert( error.reason, 'danger' );
        } else {
            let email = Meteor.user().emails[ 0 ].address;
            window.alert( `Verification sent to ${ email }!`, 'success' );
        }
        });
    },
    'click .main-detailed-post-close-button': function() {
        Template.instance().parent.removePost(this);
    },
    'mousedown, touchstart, mousemove, touchmove, mouseup, touchend, wheel': function(event, instance) {
        if (instance.parent.camera.isDragging()) {
            //Prevents interaction while dragging.
            event.preventDefault();
        }
        else {
            //Prevent events from passing through posts into the WebGL canvas.
            event.stopImmediatePropagation();
        }
    }
});

MainViewDetailedPosts = function(camera, partitioner, localPostPositions) {
    let self = this;

    //Collection of currently visible detailed posts.
    let visiblePosts = new Mongo.Collection(null);
    // Sort by position, left to right and then top to bottom
    let visiblePostsCursor = visiblePosts.find({}, {sort: {position: 1}});
    this.showFullPosts = new ReactiveVar(false);

    this.init = function(postArray) {
    };

    this.addPost = function(post) {

    };

    this.removePost = function(post) {
        let div;

        if (self.showFullPosts.get()) {
            div = $('#main-detailed-post-' + post._id);
        } else {
            div = $('#main-basic-post-' + post._id);
        }

        div.fadeOut(200, function() {
            visiblePosts.remove({_id: post._id});
        });
    };

    this.updatePost = function(id, fields) {
        if (visiblePosts.findOne({_id: id}))
            visiblePosts.update({_id: id}, {$set: fields});
    };

    this.update = function() {
        //switch between showing basic posts and detailed posts.
        if (self.showFullPosts.get()) {
            if (camera.getScale() <= 160) {
                let div = $('.main-detailed-post');

                div.fadeOut(200, function() {
                    self.showFullPosts.set(false);
                });
            }
        } else {
            if (camera.getScale() > 160) {
                let div = $('.main-basic-post');

                div.fadeOut(200, function() {
                    self.showFullPosts.set(true);
                });
            }
        }
        visiblePostsCursor.forEach(function(post) {
            if (!camera.isPointVisible(post.position) ||
                ((2 + post.replies.length) <= 5 * (1-camera.getZoomFraction()))) {
                self.removePost(post);
            }
        });

        //Add posts which are newly visible.
        let visible = partitioner.getVisible();
        for (let post of visible) {
            if (!visiblePosts.findOne({_id: post._id}) &&
                ((2 + post.replies.length) > 5 * (1-camera.getZoomFraction()))) {

                if (!post.replies || post.replies == undefined) post.replies = [];
                visiblePosts.insert(post);
            }
        }

        //Update post positions/sizes.
        var postMemo = new Map(); // Keep track of the position of each post
        visiblePostsCursor.forEach(function(post) {
            let div;

            if (self.showFullPosts.get()) {
                div = $('#main-detailed-post-' + post._id);
                div.width(POST_WIDTH*camera.getScale());
                div.css('max-height', POST_HEIGHT*camera.getScale());
            } else {
                div = $('#main-basic-post-' + post._id);
            }

            let pos = camera.toScreen(post.position);
            let pleft = pos.x - div.outerWidth()/2;
            let ptop = pos.y - div.outerHeight()/2;
            let tgl = true;
            postMemo.forEach(function(tgtPosition, tgtPost) {
              let tgtx = tgtPosition[0];
              let tgty = tgtPosition[1];
              if (pleft <= tgtx && ptop <= tgty && tgl) {
                div.css('display','none');
                div.css('max-width',1);
                div.css('max-height',1);
                tgl = false;
                //pleft += (tgtx - pleft) + 10;
                //ptop += (tgty - ptop) + 10;
              }
            });
            //div.css('left', pleft);
            //div.css('top', ptop);
            postMemo.set(post._id, [pleft + div.outerWidth(), ptop + div.outerHeight()]);
        });
    };

    this.find = function() {
        return visiblePostsCursor;
    };
};

Template.mainDetailedPostReplyButton.getParents();

Template.mainDetailedPostReplyButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        instance.parent.parent.replyTarget.set(instance.parent.data);
    }
});

Template.mainDetailedPostEditButton.getParents();

Template.mainDetailedPostEditButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        instance.parent.parent.editTarget.set(instance.parent.data);
    }
});

Template.mainDetailedPostReportButton.getParents();

Template.mainDetailedPostReportButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        instance.parent.parent.reportTarget.set(instance.parent.data);
    }
});

Template.mainDetailedPostDeleteButton.getParents();

Template.mainDetailedPostDeleteButton.events({
    'click': function(event, instance) {
        //Our parent is a mainDetailedPost, and its parent is the mainView.
        if (confirm("Are you sure you want to delete this post?")) {
            Meteor.call('deletePost', instance.parent.data._id);
        }
    }
});

Template.mainBasicPost.getParents();

Template.mainBasicPost.onCreated(function() {
    let instance = this;
    let onSubReady = new Notifier();
    this.onRendered = new Notifier();

    let user = Meteor.users.findOne({_id: Meteor.userId()});

    if (postIsSeen(instance.data))
        instance.seen = true;

    this.subscribe('post', this.data._id, this.data.poster, {onReady: onSubReady.fulfill});

    Notifier.all(onSubReady, this.onRendered).onFulfilled(function() {
        //Fade out spinner and fade in actual post.
        instance.div.children('.main-basic-post-spinner').fadeOut(100);
        instance.div.css('overflow', 'visible');
        instance.div.children('.main-basic-post-flex')
            .css('display', 'flex')
            .hide()
            .fadeIn(200);

        if (instance.data.postedOn && instance.data.poster != Meteor.userId() && Date.now() - instance.data.postedOn < (1000*60*60*24*30) && !instance.seen)
            instance.div.addClass('unseen');
    });
});

Template.mainBasicPost.onRendered(function() {
    this.div = $('#main-basic-post-' + this.data._id);
    this.div.css('display', 'flex').hide().fadeIn(200);
    setTimeout(this.onRendered.fulfill, 250);
});

Template.mainBasicPost.helpers({
    poster: function() {
        let post = Template.currentData();
        return Meteor.users.findOne({_id: post.poster});
    },
    preview: function() {
        if (Template.currentData().title) return Template.currentData().title.slice(0, 20);
        else {
            let rawContent = Template.currentData().content;
            let bbcontent, finalContent = "";
            if (rawContent) {
                bbcontent = XBBCODE.process({
                    text: rawContent,
                    removeMisalignedTags: false,
                    addInLineBreaks: true
                }).html;

                let insideTags = 0, openTags = 0, characters = 20

                while (bbcontent.length > 0 || insideTags > 0 || openTags > 0) {

                    if (bbcontent[0] == '<') {
                        insideTags++;
                        if (bbcontent[1] != '/')
                            openTags++;
                        else openTags--;
                    }

                    if (characters > 0 && insideTags < 1) {
                        if (bbcontent[0] != '\n') finalContent = finalContent + bbcontent[0];
                        else finalContent = finalContent + ' ';
                    }

                    if (insideTags <= 0) characters--;

                    if (bbcontent[0] == '>') insideTags--;

                    bbcontent = bbcontent.substr(1, bbcontent.length - 1);

                }

                return finalContent;

            }
        }
    },
    seen: function() {
        return (!this.postedOn || this.poster == Meteor.userId() || Date.now() - this.postedOn >= (1000*60*60*24*30) || Template.instance().seen);
    }
});
<|endoftext|>


<commit_before>var e = module.exports = {};

e.init = () => {
    e.category = bu.CommandType.CAT;
};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = '';
e.info = '';

e.execute = async function(msg) {
    if (msg.author.id === bu.CAT_ID) {
        await r.table('vars').get('restart').replace({
            varname: 'restart',
            varvalue: {
                channel: msg.channel.id,
                time: r.now()
            }
        }).run();
        await bu.send(msg, 'Ah! You\'ve killed me! D:');
        process.exit();
    }
};<commit_msg>verbose logging for restart<commit_after>var e = module.exports = {};

e.init = () => {
    e.category = bu.CommandType.CAT;
};
e.requireCtx = require;

e.isCommand = true;
e.hidden = false;
e.usage = '';
e.info = '';

e.execute = async function(msg) {
    if (msg.author.id === bu.CAT_ID) {
        logger.verbose('We should be going for a restart now.');
        await r.table('vars').get('restart').replace({
            varname: 'restart',
            varvalue: {
                channel: msg.channel.id,
                time: r.now()
            }
        }).run();
        logger.verbose('The restart variable has been inserted into the database.');
        await bu.send(msg, 'Ah! You\'ve killed me! D:');
        logger.verbose('We have sent the message. Calling `process.exit()` now.');
        process.exit();
    }
};<|endoftext|>


<commit_before>angular.module('14all').factory('valuesService',['dialogService',function(dialogService){
    function removeWithDlg(type,items,item){
        return dialogService.remove(type,item.title).then(function(result){
            if(result){
                item.remove().then(function(){
                    remove(items,item);
                });
            }
        });
    }

    function remove(items,item){
        items.splice(items.indexOf(item),1);
    }

    function finished(item){
        if(!item.stats)
            item.stats = {};
        item.stats.finished = !item.stats.finished;
        return update(item);
    }

    function dropped(item){
        if(!item.stats)
            item.stats = {};
        item.stats.dropped = !item.stats.dropped;
        return update(item);
    }

    function update(item){
        return item.put().then(function(updated){
            item.updatedAt = updated.updatedAt;
            return item;
        });
    }

    function validateNumber(value){
        if(angular.isNumber){
            return value;
        } else{
            return 0;
        }
    }

    function increase(value){
        value = validateNumber(value);
        value +=1;
        return value;
    }

    function decrease(value){
        value = validateNumber(value);
        if(value <= 0){
            value = 0;
        }
        else{
            value -=1;
        }
        return value;
    }

    function increaseEp(item){
        var episode = item.episode;
        item.episode = increase(item.episode);
        if(episode !== item.episode){
            return update(item);
        }
    }

    function decreaseEp(item){
        var episode = item.episode;
        item.episode = decrease(item.episode);
        if(episode !== item.episode){
            return update(item);
        }
    }

    function increaseCh(item){
        var chapter = item.chapter;
        item.chapter = increase(item.chapter);
        if(chapter !== item.chapter){
            return update(item);
        }
    }

    function decreaseCh(item){
        var chapter = item.chapter;
        item.chapter = decrease(item.chapter);
        if(chapter !== item.chapter){
            return update(item);
        }
    }

    function increaseSe(item){
        var season = item.season;
        item.season = increase(item.season);
        if(season !== item.season){
            return update(item);
        }
    }

    function decreaseSe(item){
        var season = item.season;
        item.season = decrease(item.season);
        if(season !== item.season){
            return update(item);
        }
    }

    function increasePg(item){
        var page = item.page;
        item.page = increase(item.page);
        if(page !== item.page){
            return update(item);
        }
    }

    function decreasePg(item){
        var page = item.page;
        item.page = decrease(item.page);
        if(page !== item.page){
            return update(item);
        }
    }

    var service = {
        increase: increase,
        decrease: decrease,
        increaseEp: increaseEp,
        decreaseEp: decreaseEp,
        increaseCh: increaseCh,
        decreaseCh: decreaseCh,
        increaseSe:increaseSe,
        decreaseSe:decreaseSe,
        increasePg:increasePg,
        decreasePg:decreasePg,
        update: update,
        finished: finished,
        dropped: dropped,
        remove: remove,
        removeWithDlg:removeWithDlg
    };

    return service;
}]);
<commit_msg>Added SiteIcon<commit_after>angular.module('14all').factory('valuesService',['dialogService',function(dialogService){
    function removeWithDlg(type,items,item){
        return dialogService.remove(type,item.title).then(function(result){
            if(result){
                item.remove().then(function(){
                    remove(items,item);
                });
            }
        });
    }

    function remove(items,item){
        items.splice(items.indexOf(item),1);
    }

    function finished(item){
        if(!item.stats)
            item.stats = {};
        item.stats.finished = !item.stats.finished;
        return update(item);
    }

    function dropped(item){
        if(!item.stats)
            item.stats = {};
        item.stats.dropped = !item.stats.dropped;
        return update(item);
    }

    function update(item){
        return item.put().then(function(updated){
            item.updatedAt = updated.updatedAt;
            return item;
        });
    }

    function validateNumber(value){
        if(angular.isNumber(value)){
            return value;
        } else{
            return 0;
        }
    }

    function increase(value){
        value = validateNumber(value);
        value +=1;
        return value;
    }

    function decrease(value){
        value = validateNumber(value);
        if(value <= 0){
            value = 0;
        }
        else{
            value -=1;
        }
        return value;
    }

    function increaseEp(item){
        var episode = item.episode;
        item.episode = increase(item.episode);
        if(episode !== item.episode){
            return update(item);
        }
    }

    function decreaseEp(item){
        var episode = item.episode;
        item.episode = decrease(item.episode);
        if(episode !== item.episode){
            return update(item);
        }
    }

    function increaseCh(item){
        var chapter = item.chapter;
        item.chapter = increase(item.chapter);
        if(chapter !== item.chapter){
            return update(item);
        }
    }

    function decreaseCh(item){
        var chapter = item.chapter;
        item.chapter = decrease(item.chapter);
        if(chapter !== item.chapter){
            return update(item);
        }
    }

    function increaseSe(item){
        var season = item.season;
        item.season = increase(item.season);
        if(season !== item.season){
            return update(item);
        }
    }

    function decreaseSe(item){
        var season = item.season;
        item.season = decrease(item.season);
        if(season !== item.season){
            return update(item);
        }
    }

    function increasePg(item){
        var page = item.page;
        item.page = increase(item.page);
        if(page !== item.page){
            return update(item);
        }
    }

    function decreasePg(item){
        var page = item.page;
        item.page = decrease(item.page);
        if(page !== item.page){
            return update(item);
        }
    }

    var service = {
        increase: increase,
        decrease: decrease,
        increaseEp: increaseEp,
        decreaseEp: decreaseEp,
        increaseCh: increaseCh,
        decreaseCh: decreaseCh,
        increaseSe:increaseSe,
        decreaseSe:decreaseSe,
        increasePg:increasePg,
        decreasePg:decreasePg,
        update: update,
        finished: finished,
        dropped: dropped,
        remove: remove,
        removeWithDlg:removeWithDlg
    };

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