From f2b50d931ebd61dae469a03d572187aede5e06c7 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Wed, 15 Oct 2014 20:36:12 -0400 Subject: js: Break package components into model/view/controller files. * js/packages.js: Delete. * js/model/packages.js: New file. * js/view/packages.js: New file. * js/controller/packages.js: New file. * js/routes.js: Use new namespace for packages. * guix-web/view.scm (javascripts): Add new JS files. --- js/packages.js | 387 --------------------------------------------------------- 1 file changed, 387 deletions(-) delete mode 100644 js/packages.js (limited to 'js/packages.js') diff --git a/js/packages.js b/js/packages.js deleted file mode 100644 index 3ec473a..0000000 --- a/js/packages.js +++ /dev/null @@ -1,387 +0,0 @@ -// guix-web - Web interface for GNU Guix -// Copyright © 2014 David Thompson -// -// This program is free software: you can redistribute it and/or -// modify it under the terms of the GNU Affero 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 -// Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public -// License along with this program. If not, see -// . - -var guix = guix || {}; - -guix.Packages = function() { - return m.request({ method: "GET", url: "packages.json" }); -}; - -guix.Sorter = (function() { - function Sorter(field, isDescending) { - this.field = field; - this.isDescending = _.isUndefined(isDescending) ? false : isDescending; - }; - - Sorter.prototype.sort = function(array) { - var result = _.sortBy(array, this.field); - - return this.isDescending ? result.reverse() : result; - }; - - Sorter.prototype.reverse = function() { - return new guix.Sorter(this.field, !this.isDescending); - }; - - return Sorter; -})(); - -guix.PHASE_NONE = 0; -guix.PHASE_PROMPT = 1; -guix.PHASE_DERIVATION = 2; -guix.PHASE_SUCCESS = 3; -guix.PHASE_ERROR = 4; - -guix.controller = (function() { - function controller() { - var self = this; - - this.packages = guix.Packages(); - this.pages = m.prop([]); - this.currentPageIndex = 0; - this.pageSize = 20; - this.searchTerm = m.prop(""); - this.columns = [ - { header: "Name", sortField: "name" }, - { header: "Version", sortField: "version" }, - { header: "Synopsis", sortField: "synopsis" }, - { header: "Home Page", sortField: "homepage" }, { - header: "License", - sortField: function(package) { - if(_.isArray(package.license)) { - // Concatenate all license names together for sorting. - return package.license.reduce(function(memo, l) { - return memo.concat(l.name); - }, ""); - } - - return package.license.name; - } - } - ]; - this.sorter = m.prop(new guix.Sorter("name")); - this.phase = m.prop(guix.PHASE_NONE); - this.selectedPackage = m.prop(null); - - // All packages are visible initially - this.packages.then(function(packages) { - self.pages(self.paginate(packages, self.pageSize)); - }); - }; - - - controller.prototype.paginate = function(array, pageSize) { - return guix.chunk(this.sorter().sort(array), pageSize); - }; - - controller.prototype.currentPage = function() { - return this.pages()[this.currentPageIndex] || []; - }; - - controller.prototype.isFirstPage = function() { - return this.currentPageIndex === 0; - }; - - controller.prototype.isLastPage = function() { - return this.currentPageIndex === this.pages().length - 1; - }; - - controller.prototype.isCurrentPage = function(i) { - return this.currentPageIndex === i; - }; - - controller.prototype.packageCount = function() { - return this.pages().reduce(function(memo, page) { - return memo + page.length; - }, 0); - }; - - controller.prototype.doSearch = function() { - var regexp = new RegExp(this.searchTerm(), "i"); - - this.pages(this.paginate(this.packages().filter(function(package) { - return regexp.test(package.name) || - regexp.test(package.synopsis); - }), this.pageSize)); - // Reset pagination - this.currentPageIndex = 0; - }; - - controller.prototype.sortBy = function(field) { - if(this.sorter().field === field) { - // Reverse sort order if the field is the same as before. - this.sorter(this.sorter().reverse()); - } else { - this.sorter(new guix.Sorter(field)); - } - - this.doSearch(); - }; - - controller.prototype.installSelectedPackage = function() { - var self = this; - - this.phase(guix.PHASE_DERIVATION); - - m.request({ - method: "POST", - url: "/packages/" - .concat(this.selectedPackage().name) - .concat("/install") - }).then(function() { - self.phase(guix.PHASE_SUCCESS); - }, function() { - self.phase(guix.PHASE_ERROR); - }); - }; - - return controller; -})(); - - -guix.view = function(ctrl) { - function renderName(package) { - var name = package.name; - - return m("a", { href: "/packages/".concat(name) }, name); - } - - function renderHomepage(package) { - if(package.homepage) { - return m("a", { href: package.homepage }, package.homepage); - } else { - return ""; - } - } - - function renderLicense(package) { - function licenseLink(license) { - return m("a", { href: license.uri }, license.name); - } - - if(_.isArray(package.license)) { - return m("ul.list-inline", package.license.map(function(license) { - return m("li", licenseLink(license)); - })); - } else if(package.license) { - return licenseLink(package.license); - } else { - return ""; - } - } - - function renderInstallLink(package) { - return m("a", { - href: "#", - onclick: function() { - ctrl.selectedPackage(package); - ctrl.phase(guix.PHASE_PROMPT); - return false; - } - }, "install"); - } - - function renderPackageTable() { - return m("table.table", [ - m("thead", [ - m("tr", [ - ctrl.columns.map(function(column) { - return m("th", { - class: columnHeaderClass(column), - onclick: function() { - ctrl.sortBy(column.sortField); - } - }, column.header); - }).concat([m("th", "")]) - ]) - ]), - m("tbody", [ - ctrl.currentPage().map(function(package) { - return m("tr", [ - m("td", renderName(package)), - m("td", package.version), - m("td", package.synopsis), - m("td", renderHomepage(package)), - m("td", renderLicense(package)), - m("td", renderInstallLink(package)) - ]); - }) - ]) - ]); - } - - function renderPagination() { - function renderPage(text, opts) { - return m("li", { - class: opts.class || "", - onclick: opts.onclick - }, m("a", { href: "#" }, text)); - } - - return m("ul.pagination", [ - // Back page - renderPage("«", { - class: ctrl.isFirstPage() ? "disabled" : "", - onclick: function() { - ctrl.currentPageIndex--; - - return false; - } - }) - ].concat(ctrl.pages().map(function(page, i) { - // Jump to page - return renderPage(i + 1, { - class: ctrl.isCurrentPage(i) ? "active" : "", - onclick: function() { - ctrl.currentPageIndex = i; - - return false; - } - }); - })).concat([ - // Forward page - renderPage("»", { - class: ctrl.isLastPage() ? "disabled" : "", - onclick: function() { - ctrl.currentPageIndex++; - - return false; - } - }) - ])); - } - - function renderSearchBox() { - return m("input.form-control", { - type: "text", - placeholder: "Search", - onchange: m.withAttr("value", function(value) { - ctrl.searchTerm(value); - ctrl.doSearch(); - }), - value: ctrl.searchTerm() - }); - } - - function columnHeaderClass(column) { - var sorter = ctrl.sorter(); - - if(column.sortField === sorter.field) { - return sorter.isDescending ? "sorter sort-descend" : "sorter sort-ascend"; - } - - return "sorter"; - } - - function renderModal() { - function renderBody() { - switch(ctrl.phase()) { - case guix.PHASE_PROMPT: - return [ - m("p", "Do you want to install the following packages?"), - m("ul", [ - m("li", [ - ctrl.selectedPackage().name, - " ", - ctrl.selectedPackage().version - ]) - ]) - ]; - case guix.PHASE_DERIVATION: - return [ - m("p", [ - "Installing ", - ctrl.selectedPackage().name, - " ", - ctrl.selectedPackage().version, - "..." - ]), - m(".progress", [ - m(".progress-bar.progress-bar-striped.active", { - role: "progressbar", - style: { width: "100%" } - }) - ]) - ]; - case guix.PHASE_SUCCESS: - return m(".alert.alert-success", "Installation complete!"); - case guix.PHASE_ERROR: - return m(".alert.alert-danger", "Installation failed!"); - } - - return null; - } - - function renderButtons() { - switch(ctrl.phase()) { - case guix.PHASE_PROMPT: - return [ - m(".btn.btn-default", "Cancel"), - m(".btn.btn-primary", { - onclick: function() { - ctrl.installSelectedPackage(); - m.redraw(); - } - }, "Install"), - ]; - case guix.PHASE_DERIVATION: - return m(".btn.btn-danger", "Abort"); - case guix.PHASE_SUCCESS: - case guix.PHASE_ERROR: - return m(".btn.btn-primary", { - onclick: function() { - ctrl.phase(guix.PHASE_NONE); - } - }, "Close"); - } - - return null; - } - - if(ctrl.phase() != guix.PHASE_NONE) { - return [ - m(".modal-backdrop.in"), - m("div.modal.modal-open", { - style: { - display: "block" - } - }, m(".modal-dialog", [ - m(".modal-content", [ - m(".modal-header", [ - m("h4.modal-title", "Install Packages") - ]), - m(".modal-body", renderBody()), - m(".modal-footer", renderButtons()) - ]) - ])) - ]; - } - - return null; - } - - return guix.withLayout([ - m("h2", [ - "Packages", - m("span.badge", ctrl.packageCount()) - ]), - renderModal(), - renderSearchBox(), - renderPackageTable(), - renderPagination() - ]); -}; -- cgit v1.2.3