r370 - in trunk: . chorem-webmotion chorem-webmotion/src/main/java/org/chorem/webmotion/actions chorem-webmotion/src/main/java/org/chorem/webmotion/render chorem-webmotion/src/main/resources chorem-webmotion/src/main/webapp/WEB-INF chorem-webmotion/src/main/webapp/WEB-INF/jsp chorem-webmotion/src/main/webapp/WEB-INF/jsp/bi chorem-webmotion/src/main/webapp/css chorem-webmotion/src/main/webapp/css/select2 chorem-webmotion/src/main/webapp/js
Author: bpoussin Date: 2013-07-19 15:50:40 +0200 (Fri, 19 Jul 2013) New Revision: 370 Url: http://chorem.org/projects/chorem/repository/revisions/370 Log: - implantation d'un serveur rest/wikitty (a deplacer dans le futur dans wikitty) - tout debut de bi (beaucoup de chose non package en nuiton-js car encore en test) Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/BIAction.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/WikittyRestAction.java trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/render/RenderWikitty.java trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/bi/ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/bi/reportDefinition.jsp trunk/chorem-webmotion/src/main/webapp/css/chorem-bi.css trunk/chorem-webmotion/src/main/webapp/css/ng-grid.min.css trunk/chorem-webmotion/src/main/webapp/css/select2/ trunk/chorem-webmotion/src/main/webapp/css/select2/select2-spinner.gif trunk/chorem-webmotion/src/main/webapp/css/select2/select2.css trunk/chorem-webmotion/src/main/webapp/css/select2/select2.png trunk/chorem-webmotion/src/main/webapp/css/select2/select2x2.png trunk/chorem-webmotion/src/main/webapp/js/js-hypercube.min.js trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.debug.js trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.min.js trunk/chorem-webmotion/src/main/webapp/js/ng-select2.js trunk/chorem-webmotion/src/main/webapp/js/ng-wikitty.js trunk/chorem-webmotion/src/main/webapp/js/select2.min.js Modified: trunk/chorem-webmotion/pom.xml trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/DashboardAction.java trunk/chorem-webmotion/src/main/resources/mapping trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp trunk/chorem-webmotion/src/main/webapp/WEB-INF/wro.xml trunk/pom.xml Modified: trunk/chorem-webmotion/pom.xml =================================================================== --- trunk/chorem-webmotion/pom.xml 2013-07-18 14:49:49 UTC (rev 369) +++ trunk/chorem-webmotion/pom.xml 2013-07-19 13:50:40 UTC (rev 370) @@ -148,6 +148,11 @@ <artifactId>nuiton-js-wro</artifactId> </dependency> + <dependency> + <groupId>org.nuiton.js</groupId> + <artifactId>nuiton-js-angular</artifactId> + </dependency> + <dependency> <groupId>org.nuiton.js</groupId> <artifactId>nuiton-js-jquery</artifactId> Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/BIAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/BIAction.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/BIAction.java 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,48 @@ +package org.chorem.webmotion.actions; + + +import java.util.LinkedHashSet; +import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.ChoremClient; +import org.chorem.ChoremUtil; +import org.debux.webmotion.server.WebMotionController; +import org.debux.webmotion.server.render.Render; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyExtension; + +/** + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class BIAction extends WebMotionController { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(BIAction.class); + + public Render reportDefinition(ChoremClient client, String id, String[] extension) { + log.debug("reportDefinition: " + id); + Wikitty w = client.restore(id, ".*"); + + LinkedHashSet<WikittyExtension> exts = new LinkedHashSet<WikittyExtension>(); + if (w != null) { + if (extension == null) { + exts.addAll(w.getExtensions()); + } else { + // Display the extensions and required ones for them + List<String> extensionNames = ChoremUtil.asList(null, extension); + List<WikittyExtension> wikittyExtensions = client.restoreExtensionAndDependenciesLastVesion(extensionNames); + exts.addAll(wikittyExtensions); + } + } + + + return renderView("bi/reportDefinition.jsp", "wikitty", w, "extensions", exts); + } + +} Property changes on: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/BIAction.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/DashboardAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/DashboardAction.java 2013-07-18 14:49:49 UTC (rev 369) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/DashboardAction.java 2013-07-19 13:50:40 UTC (rev 370) @@ -28,7 +28,6 @@ import java.io.InputStream; import java.io.Reader; import java.io.StringReader; -import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Calendar; Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/WikittyRestAction.java =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/WikittyRestAction.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/WikittyRestAction.java 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,140 @@ +package org.chorem.webmotion.actions; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.ChoremClient; +import org.chorem.webmotion.render.RenderWikitty; +import org.debux.webmotion.server.WebMotionController; +import org.debux.webmotion.server.render.Render; +import org.nuiton.wikitty.entities.Element; +import org.nuiton.wikitty.entities.FieldType; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyExtension; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryParser; +import org.nuiton.wikitty.query.WikittyQueryResult; +import org.nuiton.wikitty.query.conditions.Condition; + +/** + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class WikittyRestAction extends WebMotionController { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(WikittyRestAction.class); + + public Render getWikittiesFromQuery(ChoremClient client, String query) { + WikittyQuery q = WikittyQueryParser.parse(query); + WikittyQueryResult<Wikitty> qResult = client.findAllByQuery(Wikitty.class, q); + return new RenderWikitty().setModelWikitty(qResult.getAll()); + } + + public Render getWikitties(ChoremClient client, List<String> id) { + List<Wikitty> result = client.restore(id); + return new RenderWikitty().setModelWikitty(result); + } + + public Render getWikitty(ChoremClient client, String id) { + Wikitty result = client.restore(id); + return new RenderWikitty().setModelWikitty(result); + } + + public Render getWikittyFieldValue(ChoremClient client, String id, String ext, String field) { + RenderWikitty render = new RenderWikitty(); + + Wikitty result = client.restore(id); + if (StringUtils.isNotBlank(field)) { + // seulement un champs + String value = result.getFieldAsString(ext, field); + render.setModelValue(value); + } else { + Map<String, String> values = new HashMap<String, String>(); + for (String f : result.getExtensionFields(ext)) { + String value = result.getFieldAsString(ext, f); + values.put(f, value); + } + render.setModelMap(values); + } + return render; + } + + public Render select(ChoremClient client, String query) { + WikittyQuery q = WikittyQueryParser.parse(query); + WikittyQueryResult<Map<String, Object>> qResult = client.findAllByQueryAsMap(q); + return new RenderWikitty().setModelSelect(qResult.castToMap(client, String.class).getAll()); + } + + public Render facet(ChoremClient client, String query, List<String> ff, List<String> fq) { + WikittyQuery q = WikittyQueryParser.parse(query); + if (CollectionUtils.isNotEmpty(ff)) { + for (String f : ff) { + if (StringUtils.isNotBlank(f)) { + q.addFacetField(Element.get(f)); + } + } + } + if (CollectionUtils.isNotEmpty(fq)) { + for (String f : fq) { + if (StringUtils.isNotBlank(f)) { + Condition c = WikittyQueryParser.parse(f).getCondition(); + q.addFacetQuery(f, c); + } + } + } + WikittyQueryResult<Map<String, Object>> qResult = client.findAllByQueryAsMap(q); + return new RenderWikitty().setModelFacet(qResult.getFacets()); + } + + + public Render getExtensions(ChoremClient client, List<String> idOrName) { + if (CollectionUtils.isEmpty(idOrName)) { + List<String> exts = client.getAllExtensionIds(); + return new RenderWikitty().setModelValue(exts); + } else { + List<WikittyExtension> exts = new ArrayList<WikittyExtension>(idOrName.size()); + for (String i : idOrName) { + if (StringUtils.endsWith(i, "]")) { + exts.add(client.restoreExtension(i)); + } else { + exts.add(client.restoreExtensionLastVersion(i)); + } + } + return new RenderWikitty().setModelExtension(exts); + } + } + + public Render getExtension(ChoremClient client, String idOrName) { + WikittyExtension ext; + if (StringUtils.endsWith(idOrName, "]")) { + ext = client.restoreExtension(idOrName); + } else { + ext = client.restoreExtensionLastVersion(idOrName); + } + return new RenderWikitty().setModelExtension(ext); + } + + public Render getExtensionField(ChoremClient client, String idOrName, String field) { + WikittyExtension ext; + if (StringUtils.endsWith(idOrName, "]")) { + ext = client.restoreExtension(idOrName); + } else { + ext = client.restoreExtensionLastVersion(idOrName); + } + FieldType type = ext.getFieldType(field); + return new RenderWikitty().setModelValue(type); + } + + +} Property changes on: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/WikittyRestAction.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Copied: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/render/RenderWikitty.java (from rev 357, trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/render/RenderWikittyJson.java) =================================================================== --- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/render/RenderWikitty.java (rev 0) +++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/render/RenderWikitty.java 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,180 @@ +package org.chorem.webmotion.render; + +import com.google.gson.Gson; +import org.apache.commons.lang3.StringUtils; +import org.debux.webmotion.server.call.Call; +import org.debux.webmotion.server.call.HttpContext; +import org.debux.webmotion.server.mapping.Mapping; +import org.debux.webmotion.server.render.Render; +import org.nuiton.wikitty.entities.Wikitty; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import org.nuiton.wikitty.entities.WikittyExtension; +import org.nuiton.wikitty.query.FacetTopic; + +/** + * Retourne les resultats des appels REST Wikitty. + * Le resultat peut-etre en JSON, CSV (todo) ou XML (todo) suivant le header de + * la requete + * + * @author Benjamin Poussin <poussin@codelutin.com> + */ +public class RenderWikitty extends Render { + /** + * model peut-etre: + * <li>Map<String, Object> pour un wikitty + * <li>List<Map<String, Object>> pour des wikitties + * <li>Map<String, String> pour un select + * <li>Map<String, List<FacetTopic>> pour les facets + */ + protected Object model; + + public RenderWikitty() { + } + + public RenderWikitty setModelWikitty(Wikitty wikitty) { + model = createWikittyModel(wikitty); + return this; + } + + public RenderWikitty setModelWikitty(List<Wikitty> wikitties) { + List tmp = new ArrayList(wikitties.size()); + for (Wikitty w : wikitties) { + tmp.add(createWikittyModel(w)); + } + model = tmp; + return this; + } + + public RenderWikitty setModelSelect(List<Map<String, String>> select) { + model = select; + return this; + } + + public RenderWikitty setModelFacet(Map<String, List<FacetTopic>> facet) { + model = facet; + return this; + } + + public RenderWikitty setModelValue(Object value) { + model = value; + return this; + } + + public RenderWikitty setModelMap(Map values) { + model = values; + return this; + } + + public RenderWikitty setModelExtension(List<WikittyExtension> ext) { + model = ext; + return this; + } + + public RenderWikitty setModelExtension(WikittyExtension ext) { + model = ext; + return this; + } + + public Object getModel() { + return model; + } + + @Override + public void create(Mapping mapping, Call call) throws IOException, ServletException { + HttpContext context = call.getContext(); + HttpServletResponse response = context.getResponse(); + HttpServletRequest request = context.getRequest(); + // accept est de la forme: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' + String accept = request.getHeader("Accept"); + + if (accept.equals("text/xml")) { + // TODO poussin 20130702 un export en XML + throw new UnsupportedOperationException("text/xml Not support yet"); + } else if (accept.equals("text/csv")) { + // TODO poussin 20130702 un export en csv + throw new UnsupportedOperationException("text/csv Not support yet"); + } else { + // default + response.setContentType("application/json"); + + Gson gson = new Gson(); + String json = gson.toJson(model); + PrintWriter out = context.getOut(); + out.print(json); + } + + + + } + + Map<String,Object> getMeta(Wikitty wikitty){ + Map<String,Object> meta = new HashMap<String,Object>(); + meta.put("id", wikitty.getWikittyId()); + meta.put("version", wikitty.getWikittyVersion()); + meta.put("extensions", wikitty.getExtensionNames()); + + Map<String,String> displayString = new HashMap<String,String>(); + displayString.put("", wikitty.toString()); // for complete object + for (String ext : wikitty.getExtensionNames()) { + displayString.put(ext, wikitty.toString(ext)); + } + meta.put("displayString", displayString); + return meta; + } + + Map<String,Object> getData(Wikitty wikitty){ + return wikitty.getFieldValue(); + } + + void collectExtension(Wikitty wikitty, Map<String, Object> exts) { + for (WikittyExtension e : wikitty.getExtensions()) { + exts.put(e.getName(), e); + } + } + + void collectPreloaded(Wikitty wikitty, + Map<String, Object> wikitties, Map<String, Object> exts){ + Map<String,Wikitty> preloaded = wikitty.getPreloaded(); + for (Map.Entry<String, Wikitty> entry : preloaded.entrySet()) { + wikitties.put(entry.getKey(), createWikittyModel(entry.getValue(), wikitties, exts)); + } + } + + Map<String,Object> createWikittyModel(Wikitty wikitty, + Map<String, Object> wikitties, Map<String, Object> exts){ + + collectPreloaded(wikitty, wikitties, exts); + collectExtension(wikitty, exts); + + Map<String,Object> model = new HashMap<String,Object>(); + model.put("meta", getMeta(wikitty)); + model.put("data", getData(wikitty)); + + return model; + } + + Map<String,Object> createWikittyModel(Wikitty wikitty) { + Map<String, Object> wikitties = new HashMap<String, Object>(); + Map<String, Object> exts = new HashMap<String, Object>(); + + Map<String,Object> model = createWikittyModel(wikitty, wikitties, exts); + + Map<String,Object> cache = new HashMap<String,Object>(); + cache.put("wikitty", wikitties); + cache.put("extension", exts); + + model.put("cache", cache); + + return model; + } + +} Modified: trunk/chorem-webmotion/src/main/resources/mapping =================================================================== --- trunk/chorem-webmotion/src/main/resources/mapping 2013-07-18 14:49:49 UTC (rev 369) +++ trunk/chorem-webmotion/src/main/resources/mapping 2013-07-19 13:50:40 UTC (rev 370) @@ -12,6 +12,7 @@ * /sales/funnel/json/* DecoratorFilter.decorate wmDecoratorNo=true * /project/json/* DecoratorFilter.decorate wmDecoratorNo=true * /ascii/* DecoratorFilter.decorate wmDecoratorNo=true +* /rest/* DecoratorFilter.decorate wmDecoratorNo=true GET /* DecoratorFilter.decorate * /* AuthenticationFilter.check ##### @@ -54,6 +55,7 @@ * /contact view:contact.jsp * /report view:report.jsp * /ascii/budget action:DashboardAction.budget type=ascii,report=budget +* /bi/{method} action:BIAction.{method} * /hr view:hr.jsp * /hr/vacationRequest/edit/{id} action:HrAction.editVacationRequest * /hr/vacationDiv/{ids} action:HrAction.editVacationDiv @@ -77,3 +79,100 @@ * /project/employee action:project.DashboardProjectAction.requestEmployee * /crm/account/{id} action:crm.AccountAction.view * /crm/quotation/edit/{id} action:crm.QuotationAction.edit +# +# Wikitty Rest API +# +# GET +# +# retourne une liste de wikitty en json ou en csv suivant la demande dans le header +GET /rest/wikitty?q={query} action:WikittyRestAction.getWikittiesFromQuery +# +# retourne un ou plusieurs wikitty (si plusieurs fois le param id), et les retourne en json ou csv suivnatle header +GET /rest/wikitty?id={} action:WikittyRestAction.getWikitties +# +# retourne un wikitty en json +GET /rest/wikitty/{id} action:WikittyRestAction.getWikitty +# +# retourne la valeur d'une extension d'un wikitty en json +GET /rest/wikitty/{id}/{ext} action:WikittyRestAction.getWikittyFieldValue +# +# retourne la valeur d'un champs en json (pour les lists) +GET /rest/wikitty/{id}/{ext}/{field} action:WikittyRestAction.getWikittyFieldValue +# +# retourne une liste d'extensions en json, on peut avoir plusieurs fois le parametre idOrName +GET /rest/extension?idOrName={} action:WikittyRestAction.getExtensions +# +# retourne une liste d'extensions en json +GET /rest/extension action:WikittyRestAction.getExtensions +# +# retourne une extension en json +GET /rest/extension/{idOrName} action:WikittyRestAction.getExtension +# +# retourne la description d'un champs en json +GET /rest/extension/{idOrName}/{field} action:WikittyRestAction.getExtensionField +# +# retourne le resultat d'une query qui contient un select. Le resultat est en json ou csv suivant le header +GET /rest/select?q={query} action:WikittyRestAction.select +# +# retourne le resultat d'une query contenant des facets. Le resultat est en json ou csv suivant le header +# il peut y avoir des parametres: fq (facetQuery) ou ff (facetField) +GET /rest/facet?q={query} action:WikittyRestAction.facet +# +# POST pour la creation +# +# cree un nouveau ou des nouveaux wikitty (si plusieurs fois le champs json). Retourne en json les objets crees +POST /rest/wikitty?json={} action:WikittyRestAction.createWikittiesFromJson +# +# cree un nouveau ou des nouveaux wikitty. Les parametres fournissent les +# informations des champs. S'il y a plusieurs parametre 'id', alors les champs +# doivent etre prefixe par l'id de l'objet a qui ils appartiennent (c'est id, +# ne sont que des pseudos id remplacer par des vrais apres la sauvegarde). +# retourne en json les objets creer +POST /rest/wikitty action:WikittyRestAction.createWikitties +# +# cree une ou des extensions, les extensions sont sous forme de json +POST /rest/extension?json={} action:WikittyRestAction.updateExtension +# +# cree une nouvelle extension (ou version d'extension). Retourne en json, les extensions creees +POST /rest/extension action:WikittyRestAction.createExtensions +# +# PUT pour l'update +# +# modifie un ou des wikitty (si plusieurs fois le champs json). Retourne en json les objets modifies +PUT /rest/wikitty?json={} action:WikittyRestAction.updateWikittyFromJson +# +# import le fichier CSV joint a la requete. Retourne en json les objets importes +PUT /rest/wikitty?csv={} action:WikittyRestAction.updateWikittyFromJson +# +# modifie un ou des wikitty. Les parametres fournissent les informations des champs. +# S'il y a plusieurs parametre 'id', alors les champs doivent etre prefixe par +# l'id de l'objet a qui ils appartiennent. retourne en json les objets modifies +PUT /rest/wikitty action:WikittyRestAction.updateWikitty +# +# modifie un wikitty, les parametres fournissent les informations des champs +PUT /rest/wikitty/{id} action:WikittyRestAction.updateWikitty +# +# modifie un champs d'un wikitty, la valeur du champs est dans le parametre value +PUT /rest/wikitty/{id}/{fqfield}?value={} action:WikittyRestAction.updateWikittyField +# +# rem: les extensions ne sont pas modifiable +# DELETE +# +# supprime les wikitty retourne par la query, et les retourne en json +DELETE /rest/wikitty?q={query} action:WikittyRestAction.deleteWikittiesFromQuery +# +# supprime un ou plusieurs wikitty (si plusieurs fois le param id), et les retourne en json +DELETE /rest/wikitty?id={} action:WikittyRestAction.deleteWikitties +# +# supprime un wikitty et le retourne en json +DELETE /rest/wikitty/{id} action:WikittyRestAction.deleteWikitty +# +# supprime une extension d'un wikitty (rem on supporte '*' comme id ?) +DELETE /rest/wikitty/{id}/{ext} action:WikittyRestAction.deleteWikittyExtOrField +# +# vide un champs d'un wikitty (rem on supporte '*' comme id ou field ?) +DELETE /rest/wikitty/{id}/{ext}/{field} action:WikittyRestAction.deleteWikittyExtOrField +# +# supprime une extension (name) ou une version d'une extension (id), si un objet +# a encore cette extension la demande echoue. Les extensions supprimees sont retournees en json +DELETE /rest/extension/{nameOrId} action:WikittyRestAction.deleteExtension Copied: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/bi/reportDefinition.jsp (from rev 357, trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/view.jsp) =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/bi/reportDefinition.jsp (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/bi/reportDefinition.jsp 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,229 @@ +<%-- + #%L + Chorem webmotion + $Id:$ + $HeadURL:$ + %% + Copyright (C) 2011 - 2012 CodeLutin + %% + 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 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 <http://www.gnu.org/licenses/>. + #L% + --%> +<%@page contentType="text/html" pageEncoding="UTF-8"%> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/wikitty.tld" prefix="w"%> + + <script> + + var fieldCtrl = ['$scope', 'Wikitty', 'Select', 'Facet', function ($scope, Wikitty, Select, Facet) { + $scope.wikittyId = ''; + $scope.wikitty = {} + $scope.filter = ""; + $scope.select = ""; + $scope.selected = {}; + $scope.facet = {}; + $scope.data = {}; + $scope.select2 = {}; + + $scope.gridOptions = { + data: 'data', +// excludeProperties: "$$Hash", + //enablePinning: true, + enableColumnReordering: true, + showFooter: true, + showFilter: true, + //showColumnMenu: true, + showGroupPanel: true, + aggregateTemplate: + "<div ng-click=\"row.toggleExpand()\" ng-style=\"rowStyle(row)\" class=\"ngAggregate\">" + + " <span class=\"ngAggregateText\">{{row.label CUSTOM_FILTERS}} ({{sumChildren(row)}}) \#{{row.totalChildren()}}</span>" + + " <div class=\"{{row.aggClass()}}\"></div>" + + "</div>" + }; + +$scope.sumChildren = function (row) { + if (row.aggChildren.length > 0) { + var sum = 0; + var recurse = function (cur) { + if (cur.aggChildren.length > 0) { + angular.forEach(cur.aggChildren, function (a) { + recurse(a); + }); + } else { + angular.forEach(cur.children, function(c) { + sum += parseFloat(c.entity.amount); + }); + } + }; + recurse(row); + return sum; + } else { + var sum = 0; + angular.forEach(row.children, function(c) { + sum += parseFloat(c.entity.amount); + }); + return sum; + } +}; + + $scope.select2Options = { + closeOnSelect: false, + }; + + $scope.getFilter = function() { + var result = $scope.filter; + var sep = ""; + if (result != "") { + sep = " AND "; + } + angular.forEach($scope.select2, function(values, facetName) { + if (values && values.length) { + result += sep + "("; + var sep2 = ""; + angular.forEach(values, function(topic) { + result += sep2 + facetName + "='" + topic + "'"; + sep2 = " OR "; + }); + result += ")"; + sep = " AND "; + } + }); + + return result; + } + + $scope.addFacet = function(data) { + angular.forEach(data, function(facet, fieldName) { + var topicNames = facet.map(function(topic) { + return topic.topicName; + }); + + var info = $scope.wikitty.getFieldInfo(fieldName); + if (info.type === "WIKITTY") { + Wikitty.query(topicNames, function(w) { + $scope.facet[fieldName] = w.map(function(v) { + return {label: v.display(), value:v.getId()}; + }); + }); + } else { + $scope.facet[fieldName] = topicNames.map(function(v) { + return {label: v, value: v}; + }); + } + }); + }; + + $scope.selectField = function(extName, field) { + var fq = extName + '.' + field; + if ($scope.selected[fq]) { + Facet($scope.filter, fq, '', function(data) { + $scope.addFacet(data); + }); + } else { + delete $scope.facet[fq]; + } + }; + + $scope.refreshFacet = function() { + Facet($scope.filter, $scope.facet, '', function(data) { + $scope.addFacet(data); + }); + }; + + $scope.getSelectedField = function() { + var result = []; + angular.forEach($scope.selected, function(b, field) { + if (b) { + result.push(field); + } + }); + return result; + }; + + $scope.refreshData = function() { + var filter = $scope.getFilter(); + var select = angular.copy($scope.getSelectedField()); + if ($scope.select) { + select.push($scope.select); + } + var query = "select " + select.join(","); + if (filter) { + query += " where (" + filter + ")"; + } + query += " #limit 200000"; + Select(query, function(data) { + $scope.data = data.map(function (e) { + // on est oblige de remplacer les '.' par des '_' car la grid ne suppoorte pas les '.' :( + var r = {}; + for (var v in e) { + var value = e[v]; + var info = $scope.wikitty.getFieldInfo(v); + if (info && info.type === "WIKITTY") { + value = $scope.wikitty.getWikitty(value).display(); + } + var simpleName = v.replace(/.*?\./, ""); + r[simpleName] = value; + } + return r; + }); + }); + }; + + $scope.loadWikitty = function(id) { + $scope.wikittyId = id; + Wikitty.get(id, function (o) {$scope.wikitty = o}); + }; + + $scope.loadWikitty('${wikitty.id}'); + }]; + </script> + + +<div ng-app="wikitty"> + +<div ng-controller="fieldCtrl" zng-init="init('${wikitty.id}')"> + + <dl ng-repeat="extName in wikitty.getExtensionNames()"><dt>{{extName}} + <dd ng-repeat="(fieldName, fieldInfo) in wikitty.getExtension(extName).fields"> + <span ng-show="fieldInfo.type == 'DATE'"><input ng-click="selectField(extName, fieldName)" ng-model="selected[extName+'.'+fieldName]" type="checkbox"/>Year</span> + <span ng-show="fieldInfo.type == 'DATE'"><input ng-click="selectField(extName, fieldName)" ng-model="selected[extName+'.'+fieldName]" type="checkbox"/>Quater</span> + <span ng-show="fieldInfo.type == 'DATE'"><input ng-click="selectField(extName, fieldName)" ng-model="selected[extName+'.'+fieldName]" type="checkbox"/>Month</span> + <span ng-show="fieldInfo.type == 'DATE'"><input ng-click="selectField(extName, fieldName)" ng-model="selected[extName+'.'+fieldName]" type="checkbox"/>Week</span> + <span ng-show="fieldInfo.type == 'DATE'"><input ng-click="selectField(extName, fieldName)" ng-model="selected[extName+'.'+fieldName]" type="checkbox"/>Day</span> + <input ng-click="selectField(extName, fieldName)" ng-model="selected[extName+'.'+fieldName]" type="checkbox"/> + {{fieldName}} : {{wikitty.display(extName, fieldName)}} + <i ng-show="fieldInfo.type == 'WIKITTY'" class="icon-plus icon-black"></i> + </dd> + </dl> + +<div class="container"> + <input type="text" name="filter" ng-change="refreshFacet()" ng-model="filter" placeholder="filter"/> + <input type="text" name="return" ng-model="select" placeholder="return value"/> +</div> + + <div ng-repeat="(field, topics) in facet"> + <h3>{{field}}</h3> + <select class="select" ui-select2="select2Options" ng-model="select2[field]" multiple="true"> + <option ng-repeat="t in topics | orderBy:'t.label'" value="{{t.value}}"/>{{t.label}}</option> + </select> + </div> + + <button ng-click='refreshData()'>Refresh</button> + + <div class="gridStyle" ng-grid="gridOptions"></div> + +</div> + + +</div> \ No newline at end of file Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp 2013-07-18 14:49:49 UTC (rev 369) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp 2013-07-19 13:50:40 UTC (rev 370) @@ -40,6 +40,9 @@ <link href="<c:url value='/css/chorem.less'/>" rel="stylesheet/less"> <link href="<c:url value='/css/chorem-crm.css'/>" rel="stylesheet" type="text/css"/> <link href="<c:url value='/css/chorem-sales.css'/>" rel="stylesheet" type="text/css"/> + <link href="<c:url value='/css/chorem-bi.css'/>" rel="stylesheet" type="text/css"/> + <link href="<c:url value='/css/ng-grid.min.css'/>" rel="stylesheet" type="text/css"/> + <link href="<c:url value='/css/select2/select2.css'/>" rel="stylesheet" type="text/css"/> <script type="text/javascript"> var webContext = "<c:url value='/'/>"; @@ -49,7 +52,12 @@ <script type="text/javascript" src="<c:url value='/nuiton-js/chorem-lib.js'/>"></script> <script type="text/javascript" src="<c:url value='/js/chorem.js'/>"></script> - </head> + <script type="text/javascript" src="<c:url value='/js/ng-wikitty.js'/>"></script> + <script type="text/javascript" src="<c:url value='/js/js-hypercube.min.js'/>"></script> + <script type="text/javascript" src="<c:url value='/js/ng-grid-2.0.7.debug.js'/>"></script> + <script type="text/javascript" src="<c:url value='/js/select2.min.js'/>"></script> + <script type="text/javascript" src="<c:url value='/js/ng-select2.js'/>"></script> + </head> <body> <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/wro.xml =================================================================== --- trunk/chorem-webmotion/src/main/webapp/WEB-INF/wro.xml 2013-07-18 14:49:49 UTC (rev 369) +++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/wro.xml 2013-07-19 13:50:40 UTC (rev 370) @@ -17,6 +17,9 @@ <group-ref>jqplot.barRenderer</group-ref> <group-ref>jqplot.pieRenderer</group-ref> + <group-ref>angular</group-ref> + <group-ref>angular-resource</group-ref> + <js>/js/jquery-ui-timepicker-addon.js</js> <js>/js/jquery-ui-timepicker-fr.js</js> Added: trunk/chorem-webmotion/src/main/webapp/css/chorem-bi.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/css/chorem-bi.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/css/chorem-bi.css 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,9 @@ +.gridStyle { + border: 1px solid rgb(212,212,212); + width: 800px; + height: 400px +} + +.select { + width: 800px; +} \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/css/ng-grid.min.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/css/ng-grid.min.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/css/ng-grid.min.css 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1 @@ +.ngGrid{background-color:#fdfdfd}.ngGrid input[type="checkbox"]{margin:0;padding:0}.ngGrid input{vertical-align:top}.ngGrid.unselectable{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.ngViewport{overflow:auto;min-height:20px}.ngViewport:focus{outline:0}.ngCanvas{position:relative}.ngVerticalBar{position:absolute;right:0;width:0}.ngVerticalBarVisible{width:1px;background-color:#d4d4d4}.ngHeaderContainer{position:relative;overflow:hidden;font-weight:bold;background-color:inherit}.ngHeaderCell{position:absolute;top:0;bottom:0;background-color:inherit}.ngHeaderCell.pinned{z-index:1}.ngHeaderSortColumn{position:absolute;overflow:hidden}.ngTopPanel{position:relative;z-index:1;background-color:#eaeaea;border-bottom:1px solid #d4d4d4}.ngSortButtonDown{position:absolute;top:3px;left:0;right:0;margin-left:auto;margin-right:auto;border-color:gray transparent;border-style:solid;border-width:0 5px 5px 5px;height:0;width:0}.ngNoSort{cursor:default}.ngHeaderButton{position:absolute;right:2px;top:8px;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;width:14px;height:14px;z-index:1;background-color:#9fbbb4;cursor:pointer}.ngSortButtonUp{position:absolute;top:3px;left:0;right:0;margin-left:auto;margin-right:auto;border-color:gray transparent;border-style:solid;border-width:5px 5px 0 5px;height:0;width:0}.ngHeaderScroller{position:absolute;background-color:inherit}.ngSortPriority{position:absolute;top:-5px;left:1px;font-size:6pt;font-weight:bold}.ngHeaderGrip{cursor:col-resize;width:10px;right:-5px;top:0;height:100%;position:absolute;background-color:transparent}.ngHeaderText{padding:5px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden}.ngHeaderButtonArrow{position:absolute;top:4px;left:3px;width:0;height:0;border-style:solid;border-width:6.5px 4.5px 0 4.5px;border-color:#4d4d4d transparent transparent transparent}.ngPinnedIcon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTAw9HKhAAAAmElEQVQoU33PQapBURjA8UtkwJuaWYGSgfQWYBMvczPmTCzAAGVuaA228BZhRCkDGSmE31FucuRfvzq3vr5zT/JSjSU7DsypEPXDkDVn2hSIytJhw4kWGaLCxgHh2gt/RBuLzNhz5caWPjnSqqw4EraFfwznf8qklWjwy4IRTerkiQoPGtPl40OehcEJvcfXl8LglLfBJLkDcMgbgHlHhK8AAAAASUVORK5CYII=);background-repeat:no-repeat;position:absolute;right:5px;top:5px;height:10px;width:10px}.ngUnPinnedIcon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTAw9HKhAAAAlElEQVQoU33PPQrCQBRF4fFnI2KfZVi5ARvdgo1l6mwmkCJVOgluwd5OwUoDtnoOxAei8cLXTN7cvEl/skCNDCMPfsUPO5zQwOHIDEvYtMURHe6wOVLgigvOePRyeDkyR4ln7wZ//7XfFBu8B23+aDJjrHGAwza7hjtHJvDmHg7b7Bru7AMjK7Rw2ObBVHDY5oGk9AKQNB2zy8MBTgAAAABJRU5ErkJggg==);background-repeat:no-repeat;position:absolute;height:10px;width:10px;right:5px;top:5px}.ngColMenu{right:2px;padding:5px;top:25px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#bdd0cb;position:absolute;border:2px solid #d4d4d4;z-index:1}.ngColListCheckbox{position:relative;right:3px;top:4px}.ngColList{list-style-type:none}.ngColListItem{position:relative;right:17px;top:2px;white-space:nowrap}.ngMenuText{position:relative;top:2px;left:2px}.ngGroupPanel{background-color:#eaeaea;overflow:hidden;border-bottom:1px solid #d4d4d4}.ngGroupPanelDescription{margin-top:5px;margin-left:5px}.ngGroupList{list-style-type:none;margin:0;padding:0}.ngAggHeader{position:absolute;border:0}.ngGroupElement{float:left;height:100%;width:100%}.ngGroupIcon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAEFJREFUKFNjoAhISkr+h2J5JDZODNXGwGBsbPwfhIGAA8bGh6HaGBiAGhxAGJmND4M1gQCSM0adCsVQbcPcqQwMALWDGyDvWPefAAAAAElFTkSuQmCC);background-repeat:no-repeat;height:15px;width:15px;position:absolute;right:-2px;top:2px}.ngGroupedByIcon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAElJREFUKFNjoAhISkr+R8LyaHwMDNXGwGBsbPwfhoGAA5mPDUO1oWpE52PDYE0gALTFAYbR+dgwWBMIoPlh1I9ADNU2NPzIwAAAFQYI9E4OLvEAAAAASUVORK5CYII=);background-repeat:no-repeat;height:15px;width:15px;position:absolute;right:-2px;top:2px}.ngGroupName{background-color:#fdfdfd;border:1px solid #d4d4d4;padding:3px 10px;float:left;margin-left:0;margin-top:2px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;font-weight:bold}.ngGroupArrow{width:0;height:0;border-top:6px solid transparent;border-bottom:6px solid transparent;border-left:6px solid black;margin-top:10px;margin-left:5px;margin-right:5px;float:right}.ngGroupingNumber{position:absolute;right:-10px;top:-2px}.ngAggArrowCollapsed{position:absolute;left:8px;bottom:10px;width:0;height:0;border-style:solid;border-width:5px 0 5px 8.7px;border-color:transparent transparent transparent #000}.ngGroupItem{float:left}.ngGroupItem:first-child{margin-left:2px}.ngRemoveGroup{width:5px;-moz-opacity:.4;opacity:.4;margin-top:-1px;margin-left:5px}.ngRemoveGroup:hover{color:black;text-decoration:none;cursor:pointer;-moz-opacity:.7;opacity:.7}.ngAggArrowExpanded{position:absolute;left:8px;bottom:10px;width:0;height:0;border-style:solid;border-width:0 0 9px 9px;border-color:transparent transparent #000 transparent}.ngAggregate{position:absolute;background-color:#c9dde1;border-bottom:1px solid beige;overflow:hidden;top:0;bottom:0;right:-1px;left:0}.ngAggregateText{position:absolute;left:27px;top:5px;line-height:20px;white-space:nowrap}.ngRow{position:absolute;border-bottom:1px solid #d4d4d4}.ngRow.odd{background-color:#fdfdfd}.ngRow.even{background-color:#f3f3f3}.ngRow.selected{background-color:#c9dde1}.ngCell{overflow:hidden;position:absolute;top:0;bottom:0;background-color:inherit}.ngCell.pinned{z-index:1}.ngCellText{padding:5px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden}.ngSelectionCell{margin-top:9px;margin-left:6px}.ngSelectionHeader{position:absolute;top:11px;left:6px}.ngCellElement:focus{outline:0;background-color:#b3c4c7}.ngRow.canSelect{cursor:pointer}.ngSelectionCheckbox{margin-top:9px;margin-left:6px}.ngFooterPanel{background-color:#eaeaea;padding:0;border-top:1px solid #d4d4d4;position:relative}.nglabel{display:block;float:left;font-weight:bold;padding-right:5px}.ngTotalSelectContainer{float:left;margin:5px;margin-top:7px}.ngFooterSelectedItems{padding:2px}.ngFooterTotalItems.ngnoMultiSelect{padding:0!important}.ngPagerFirstBar{width:10px;border-left:2px solid #4d4d4d;margin-top:-6px;height:12px;margin-left:-3px}.ngPagerButton{height:25px;min-width:26px}.ngPagerFirstTriangle{width:0;height:0;border-style:solid;border-width:5px 8.7px 5px 0;border-color:transparent #4d4d4d transparent transparent;margin-left:2px}.ngPagerNextTriangle{margin-left:1px}.ngPagerPrevTriangle{margin-left:0}.ngPagerLastTriangle{width:0;height:0;border-style:solid;border-width:5px 0 5px 8.7px;border-color:transparent transparent transparent #4d4d4d;margin-left:-1px}.ngPagerLastBar{width:10px;border-left:2px solid #4d4d4d;margin-top:-6px;height:12px;margin-left:1px}.ngFooterTotalItems{padding:2px} \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/css/select2/select2-spinner.gif =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/css/select2/select2-spinner.gif ___________________________________________________________________ Added: svn:mime-type + image/gif Added: trunk/chorem-webmotion/src/main/webapp/css/select2/select2.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/css/select2/select2.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/css/select2/select2.css 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,680 @@ +/* +Version: 3.4.1 Timestamp: Thu Jun 27 18:02:10 PDT 2013 +*/ +.select2-container { + margin: 0; + position: relative; + display: inline-block; + /* inline-block for ie7 */ + zoom: 1; + *display: inline; + vertical-align: middle; +} + +.select2-container, +.select2-drop, +.select2-search, +.select2-search input{ + /* + Force border-box so that % widths fit the parent + container without overlap because of margin/padding. + + More Info : http://www.quirksmode.org/css/box.html + */ + -webkit-box-sizing: border-box; /* webkit */ + -khtml-box-sizing: border-box; /* konqueror */ + -moz-box-sizing: border-box; /* firefox */ + -ms-box-sizing: border-box; /* ie */ + box-sizing: border-box; /* css3 */ +} + +.select2-container .select2-choice { + display: block; + height: 26px; + padding: 0 0 0 8px; + overflow: hidden; + position: relative; + + border: 1px solid #aaa; + white-space: nowrap; + line-height: 26px; + color: #444; + text-decoration: none; + + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%); + background-image: -ms-linear-gradient(top, #ffffff 0%, #eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0); + background-image: linear-gradient(top, #ffffff 0%, #eeeeee 50%); +} + +.select2-container.select2-drop-above .select2-choice { + border-bottom-color: #aaa; + + -webkit-border-radius:0 0 4px 4px; + -moz-border-radius:0 0 4px 4px; + border-radius:0 0 4px 4px; + + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%); + background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%); +} + +.select2-container.select2-allowclear .select2-choice .select2-chosen { + margin-right: 42px; +} + +.select2-container .select2-choice > .select2-chosen { + margin-right: 26px; + display: block; + overflow: hidden; + + white-space: nowrap; + + -ms-text-overflow: ellipsis; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; +} + +.select2-container .select2-choice abbr { + display: none; + width: 12px; + height: 12px; + position: absolute; + right: 24px; + top: 8px; + + font-size: 1px; + text-decoration: none; + + border: 0; + background: url('select2.png') right top no-repeat; + cursor: pointer; + outline: 0; +} + +.select2-container.select2-allowclear .select2-choice abbr { + display: inline-block; +} + +.select2-container .select2-choice abbr:hover { + background-position: right -11px; + cursor: pointer; +} + +.select2-drop-undermask { + border: 0; + margin: 0; + padding: 0; + position: absolute; + left: 0; + top: 0; + z-index: 9998; + background-color: transparent; + filter: alpha(opacity=0); +} + +.select2-drop-mask { + border: 0; + margin: 0; + padding: 0; + position: absolute; + left: 0; + top: 0; + z-index: 9998; + /* styles required for IE to work */ + background-color: #fff; + opacity: 0; + filter: alpha(opacity=0); +} + +.select2-drop { + width: 100%; + margin-top: -1px; + position: absolute; + z-index: 9999; + top: 100%; + + background: #fff; + color: #000; + border: 1px solid #aaa; + border-top: 0; + + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; + + -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 4px 5px rgba(0, 0, 0, .15); +} + +.select2-drop-auto-width { + border-top: 1px solid #aaa; + width: auto; +} + +.select2-drop-auto-width .select2-search { + padding-top: 4px; +} + +.select2-drop.select2-drop-above { + margin-top: 1px; + border-top: 1px solid #aaa; + border-bottom: 0; + + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; + + -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); +} + +.select2-drop-active { + border: 1px solid #5897fb; + border-top: none; +} + +.select2-drop.select2-drop-above.select2-drop-active { + border-top: 1px solid #5897fb; +} + +.select2-container .select2-choice .select2-arrow { + display: inline-block; + width: 18px; + height: 100%; + position: absolute; + right: 0; + top: 0; + + border-left: 1px solid #aaa; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; + + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + background: #ccc; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); + background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); + background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0); + background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%); +} + +.select2-container .select2-choice .select2-arrow b { + display: block; + width: 100%; + height: 100%; + background: url('select2.png') no-repeat 0 1px; +} + +.select2-search { + display: inline-block; + width: 100%; + min-height: 26px; + margin: 0; + padding-left: 4px; + padding-right: 4px; + + position: relative; + z-index: 10000; + + white-space: nowrap; +} + +.select2-search input { + width: 100%; + height: auto !important; + min-height: 26px; + padding: 4px 20px 4px 5px; + margin: 0; + + outline: 0; + font-family: sans-serif; + font-size: 1em; + + border: 1px solid #aaa; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + + background: #fff url('select2.png') no-repeat 100% -22px; + background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%); +} + +.select2-drop.select2-drop-above .select2-search input { + margin-top: 4px; +} + +.select2-search input.select2-active { + background: #fff url('select2-spinner.gif') no-repeat 100%; + background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2-spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('select2-spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%); +} + +.select2-container-active .select2-choice, +.select2-container-active .select2-choices { + border: 1px solid #5897fb; + outline: none; + + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow: 0 0 5px rgba(0,0,0,.3); + box-shadow: 0 0 5px rgba(0,0,0,.3); +} + +.select2-dropdown-open .select2-choice { + border-bottom-color: transparent; + -webkit-box-shadow: 0 1px 0 #fff inset; + -moz-box-shadow: 0 1px 0 #fff inset; + box-shadow: 0 1px 0 #fff inset; + + -webkit-border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; + + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; + + background-color: #eee; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); + background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); + background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); +} + +.select2-dropdown-open.select2-drop-above .select2-choice, +.select2-dropdown-open.select2-drop-above .select2-choices { + border: 1px solid #5897fb; + border-top-color: transparent; + + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -webkit-linear-gradient(center top, white 0%, #eeeeee 50%); + background-image: -moz-linear-gradient(center top, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%); + background-image: -ms-linear-gradient(bottom, #ffffff 0%,#eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); + background-image: linear-gradient(bottom, #ffffff 0%,#eeeeee 50%); +} + +.select2-dropdown-open .select2-choice .select2-arrow { + background: transparent; + border-left: none; + filter: none; +} +.select2-dropdown-open .select2-choice .select2-arrow b { + background-position: -18px 1px; +} + +/* results */ +.select2-results { + max-height: 200px; + padding: 0 0 0 4px; + margin: 4px 4px 4px 0; + position: relative; + overflow-x: hidden; + overflow-y: auto; + -webkit-tap-highlight-color: rgba(0,0,0,0); +} + +.select2-results ul.select2-result-sub { + margin: 0; + padding-left: 0; +} + +.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px } +.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px } + +.select2-results li { + list-style: none; + display: list-item; + background-image: none; +} + +.select2-results li.select2-result-with-children > .select2-result-label { + font-weight: bold; +} + +.select2-results .select2-result-label { + padding: 3px 7px 4px; + margin: 0; + cursor: pointer; + + min-height: 1em; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.select2-results .select2-highlighted { + background: #3875d7; + color: #fff; +} + +.select2-results li em { + background: #feffde; + font-style: normal; +} + +.select2-results .select2-highlighted em { + background: transparent; +} + +.select2-results .select2-highlighted ul { + background: white; + color: #000; +} + + +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-selection-limit { + background: #f4f4f4; + display: list-item; +} + +/* +disabled look for disabled choices in the results dropdown +*/ +.select2-results .select2-disabled.select2-highlighted { + color: #666; + background: #f4f4f4; + display: list-item; + cursor: default; +} +.select2-results .select2-disabled { + background: #f4f4f4; + display: list-item; + cursor: default; +} + +.select2-results .select2-selected { + display: none; +} + +.select2-more-results.select2-active { + background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%; +} + +.select2-more-results { + background: #f4f4f4; + display: list-item; +} + +/* disabled styles */ + +.select2-container.select2-container-disabled .select2-choice { + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container.select2-container-disabled .select2-choice .select2-arrow { + background-color: #f4f4f4; + background-image: none; + border-left: 0; +} + +.select2-container.select2-container-disabled .select2-choice abbr { + display: none; +} + + +/* multiselect */ + +.select2-container-multi .select2-choices { + height: auto !important; + height: 1%; + margin: 0; + padding: 0; + position: relative; + + border: 1px solid #aaa; + cursor: text; + overflow: hidden; + + background-color: #fff; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%); +} + +.select2-locked { + padding: 3px 5px 3px 5px !important; +} + +.select2-container-multi .select2-choices { + min-height: 26px; +} + +.select2-container-multi.select2-container-active .select2-choices { + border: 1px solid #5897fb; + outline: none; + + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow: 0 0 5px rgba(0,0,0,.3); + box-shadow: 0 0 5px rgba(0,0,0,.3); +} +.select2-container-multi .select2-choices li { + float: left; + list-style: none; +} +.select2-container-multi .select2-choices .select2-search-field { + margin: 0; + padding: 0; + white-space: nowrap; +} + +.select2-container-multi .select2-choices .select2-search-field input { + padding: 5px; + margin: 1px 0; + + font-family: sans-serif; + font-size: 100%; + color: #666; + outline: 0; + border: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + background: transparent !important; +} + +.select2-container-multi .select2-choices .select2-search-field input.select2-active { + background: #fff url('select2-spinner.gif') no-repeat 100% !important; +} + +.select2-default { + color: #999 !important; +} + +.select2-container-multi .select2-choices .select2-search-choice { + padding: 3px 5px 3px 18px; + margin: 3px 0 3px 5px; + position: relative; + + line-height: 13px; + color: #333; + cursor: default; + border: 1px solid #aaaaaa; + + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + + -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + -moz-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0 ); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); +} +.select2-container-multi .select2-choices .select2-search-choice .select2-chosen { + cursor: default; +} +.select2-container-multi .select2-choices .select2-search-choice-focus { + background: #d4d4d4; +} + +.select2-search-choice-close { + display: block; + width: 12px; + height: 13px; + position: absolute; + right: 3px; + top: 4px; + + font-size: 1px; + outline: none; + background: url('select2.png') right top no-repeat; +} + +.select2-container-multi .select2-search-choice-close { + left: 3px; +} + +.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { + background-position: right -11px; +} +.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { + background-position: right -11px; +} + +/* disabled styles */ +.select2-container-multi.select2-container-disabled .select2-choices{ + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { + padding: 3px 5px 3px 5px; + border: 1px solid #ddd; + background-image: none; + background-color: #f4f4f4; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none; + background:none; +} +/* end multiselect */ + + +.select2-result-selectable .select2-match, +.select2-result-unselectable .select2-match { + text-decoration: underline; +} + +.select2-offscreen, .select2-offscreen:focus { + clip: rect(0 0 0 0); + width: 1px; + height: 1px; + border: 0; + margin: 0; + padding: 0; + overflow: hidden; + position: absolute; + outline: 0; + left: 0px; +} + +.select2-display-none { + display: none; +} + +.select2-measure-scrollbar { + position: absolute; + top: -10000px; + left: -10000px; + width: 100px; + height: 100px; + overflow: scroll; +} +/* Retina-ize icons */ + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) { + .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice .select2-arrow b { + background-image: url('select2x2.png') !important; + background-repeat: no-repeat !important; + background-size: 60px 40px !important; + } + .select2-search input { + background-position: 100% -21px !important; + } +} Added: trunk/chorem-webmotion/src/main/webapp/css/select2/select2.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/css/select2/select2.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/css/select2/select2x2.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/css/select2/select2x2.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/js-hypercube.min.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/js-hypercube.min.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/js-hypercube.min.js 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1 @@ +var ps=Object.create?Object.create(null):{};ps.addSingletonGetter=function(a){a.getInstance=function(){return a.instance_||a.setInstance.apply(this,arguments)};a.hasInstance=function(){return !!a.instance_};a.setInstance=function(){return a.instance_=new a(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7])};a.deleteInstance=function(){a.instance_=null}};ps.addCloning=function(a){a.prototype.clone=function(){var c=$.extend(true,new a(),this);var b=function(e){var d;for(name in e){d=e[name];if(!d instanceof Object){continue}else{if(d===e){continue}else{if(d.clone){e[name]=d.clone();continue}}}b(d)}};b(c);return c}};ps.inherits=function(b,a){function c(){}c.prototype=a.prototype;b.superClass_=a.prototype;b.prototype=new c();b.prototype.constructor=b};ps.time=function(){return Math.floor(ps.microTime()/1000)};ps.microTime=Date.now||(function(){return +new Date()});ps.timezone=function(){return Math.floor((new Date()).getTimezoneOffset()/60)*-1};ps.obj=function(){return Object.create?Object.create(null):{}};ps.isDef=function(a){return a!==undefined};ps.isNull=function(a){return a===null};ps.isDefAndNotNull=function(a){return a!=null};ps.isString=function(a){return typeof a=="string"};ps.isBoolean=function(a){return typeof a=="boolean"};ps.isNumber=function(a){return(typeof a=="number")&&!isNaN(a)&&isFinite(a)};ps.isFunction=function(a){return $.isFunction(a)};ps.isObject=function(a){return typeof a==="object"};ps.isPlainObject=function(a){return $.isPlainObject(a)};ps.isArray=function(a){return $.isArray(a)};ps.isElement=function(a){return ps.isObject(a)&&a.nodeType===1};ps.uid=function(){return ps._uidCounter++};ps._uidCounter=1000;ps.uniqueElementId=function(){return"_idgen_"+ps.uid()};ps.redirect=function(a){if(a){window.document.location.replace(a)}else{window.document.location.reload(true)}};ps.navigateTo=function(a){try{window.document.location.href=a}catch(b){}};ps.array={contains:function(d,c){for(var b=0,a=d.length;b<a;++b){if(d[b]===c){return true}}return false},unique:function(d){var c=[];for(var b=0,a=d.length;b<a;++b){if(!ps.array.contains(c,d[b])){c.push(d[b])}}return c},diff:function(f,e){var d=[],g,b=ps.array.pivot(e);for(var c=0,a=f.length;c<a;++c){g=f[c];if(!ps.isDef(b[g])){d.push(g)}}return d},intersect:function(e,d){var c=[];for(var b=0,a=e.length;b<a;++b){if(ps.array.contains(d,e[b])){c.push(e[b])}}return c},union:function(b,a){a=Array.prototype.slice.apply(arguments).slice(1);return Array.prototype.concat.apply(b,a)},clone:function(a){return ps.array.union([],a)},keys:function(b){var a=[];$.each(b,function(d,c){a.push(d)});return a},vals:function(b){var a=[];$.each(b,function(d,c){a.push(c)});return a},combine:function(d,a){var c=(d.length<=a.length?d.length:a.length),e=ps.obj();for(var b=0;b<c;++b){e[d[b]]=a[b]}return e},pivot:function(b){var a=ps.obj();$.each(b,function(d,c){a[c]=d});return a},randSort:function(a){a.sort(function(d,c){return 0.5-Math.random()});return a}};ps.object={contains:ps.array.contains,keys:ps.array.keys,vals:ps.array.vals,combine:ps.array.combine,fill:function(c,d){var e=ps.obj();for(var b=0,a=c.length;b<a;++b){e[c[b]]=d}return e}};ps.FactIndex=function(){this._slabs=ps.obj();this._factValues=[]};ps.FactIndex.prototype.getFactValues=function(){if(!this._factValues.length){this._factValues=ps.array.keys(this._slabs);this._factValues.sort()}return this._factValues};ps.FactIndex.prototype.get=function(a){return this._slabs[a]};ps.FactIndex.prototype.insert=function(c,b){this._factValues=[];var a=this._slabs[c];if(!ps.isDef(a)){a=[];this._slabs[c]=a}a.push(b)};ps.Cell=function(c,b,a){this.time=a?a:null;this.facts=c;this.measures=b};ps.addCloning(ps.Cell);ps.Cell.prototype.value=function(a){return ps.isNumber(this.measures[a])?this.measures[a]:0};ps.Cell.aggregate=function(b,d){var e=ps.obj();if(!b||!b.length){return e}for(var c=0,a=b.length;c<a;++c){$.each(b[c].measures,function(f,g){e[f]=d(e[f],g)})}return e};ps.Cell.getComparisonFn=function(a,b){return function(d,c){var e=d.value(a)-c.value(a);return b?e*-1:e}};ps.Cube=function(a){this._indecies=ps.obj();this._cells=[];this._factNames=[];this._measureNames=ps.isArray(a)?a:null};ps.Cube.prototype.count=function(){return this._cells.length};ps.Cube.prototype.getFactNames=function(){if(!this._factNames.length){this._factNames=ps.array.keys(this._indecies);this._factNames.sort()}return this._factNames};ps.Cube.prototype.getValues=function(d){var b=[];for(var c=0,a=this._cells.length;c<a;++c){value=this._cells[c].facts[d];if(value){b.push(value)}}return ps.array.unique(b)};ps.Cube.prototype.insert=function(b){this._factNames=[];var a=this._cells.length,c;$.each(b.facts,$.proxy(function(e,d){c=this._indecies[e];if(!ps.isDef(c)){c=new ps.FactIndex();this._indecies[e]=c}c.insert(d,a)},this));this._cells.push(b)};ps.Cube.prototype._getPos=function(d){var a,c,b;$.each(d,$.proxy(function(f,e){b=this._indecies[f];if(!ps.isDef(b)){a=[];return false}c=b.get(e);if(!ps.isDef(c)){a=[];return false}if(a){a=ps.array.intersect(a,c)}else{a=c}if(!a.length){a=[];return false}},this));return a};ps.Cube.prototype.slice=function(d){var e=new ps.Cube(this._measureNames),a=this._getPos(d);for(var c=0,b=a.length;c<b;++c){e.insert(this._cells[a[c]])}return e};ps.Cube.prototype.sliceDates=function(g,e){var a=new ps.Cube(this._measureNames),c=this._cells,f;for(var d=0,b=c.length;d<b;++d){if(c[d].time>=g&&c[d].time<e){a.insert(c[d])}}return a};ps.Cube.prototype.dice=function(e){var c=new ps.Cube(this._measureNames),a=this._getPos(e);a=ps.array.diff(ps.array.keys(this._cells),a);for(var d=0,b=a.length;d<b;++d){c.insert(this._cells[a[d]])}return c};ps.Cube.prototype.merge=function(a){for(var c=0,b=a._cells.length;c<b;++c){this.insert(a._cells[c])}};ps.Cube.prototype.sum=function(a){if(!this._cells.length){if(this._measureNames){return ps.object.fill(this._measureNames,0)}return ps.obj()}return ps.Cell.aggregate(this._cells,function(b,c){if(ps.isNumber(a)){c=parseFloat(c.toPrecision(a),10)}if(ps.isDef(b)){b+=c}else{b=c}return b})};ps.Cube.prototype.avg=function(d,a){var c=this.sum(a),e=ps.obj(),b=false;$.each(c,function(f,g){b=true;g=g?g/d:g;if(ps.isNumber(a)){g=parseFloat(g.toPrecision(a),10)}e[f]=g});if(!b&&this._measureNames){return ps.object.fill(this._measureNames,0)}return e};ps.Cube.prototype.serialize=function(){var d=[],e,a;for(var c=0,b=this._cells.length;c<b;++c){a=this._cells[c];e=ps.obj();e.facts=a.facts;e.measures=a.measures;d.push(e)}return d};ps.Cube.deserialize=function(f,e){var a=new ps.Cube(e),c;for(var d=0,b=f.length;d<b;++d){c=f[d];a.insert(new ps.Cell(c.facts,c.measures,c.time*1000))}return a};ps.Cube.transforms=ps.obj();ps.Cube.transforms.dateLocal=function(b){var a=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];$.each(b,function(e,c){if(!c.time||!c.facts){console.error("record is missing necessary properties. halting.",c);return false}var d=new Date(c.time*1000);c.facts.year=d.getFullYear();c.facts.month=d.getMonth()+1;c.facts.day=d.getDate();c.facts.hour=d.getHours();c.facts.day_of_week=a[d.getDay()]})}; \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.debug.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.debug.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.debug.js 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,3588 @@ +/*********************************************** +* ng-grid JavaScript Library +* Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md +* License: MIT (http://www.opensource.org/licenses/mit-license.php) +* Compiled At: 07/02/2013 23:01 +***********************************************/ +(function(window, $) { +'use strict'; +// the # of rows we want to add to the top and bottom of the rendered grid rows +var EXCESS_ROWS = 6; +var SCROLL_THRESHOLD = 4; +var ASC = "asc"; +// constant for sorting direction +var DESC = "desc"; +// constant for sorting direction +var NG_FIELD = '_ng_field_'; +var NG_DEPTH = '_ng_depth_'; +var NG_HIDDEN = '_ng_hidden_'; +var NG_COLUMN = '_ng_column_'; +var CUSTOM_FILTERS = /CUSTOM_FILTERS/g; +var COL_FIELD = /COL_FIELD/g; +var DISPLAY_CELL_TEMPLATE = /DISPLAY_CELL_TEMPLATE/g; +var EDITABLE_CELL_TEMPLATE = /EDITABLE_CELL_TEMPLATE/g; +var TEMPLATE_REGEXP = /<.+>/; +window.ngGrid = {}; +window.ngGrid.i18n = {}; + +// Declare app level module which depends on filters, and services +var ngGridServices = angular.module('ngGrid.services', []); +var ngGridDirectives = angular.module('ngGrid.directives', []); +var ngGridFilters = angular.module('ngGrid.filters', []); +// initialization of services into the main module +angular.module('ngGrid', ['ngGrid.services', 'ngGrid.directives', 'ngGrid.filters']); +//set event binding on the grid so we can select using the up/down keys +var ngMoveSelectionHandler = function($scope, elm, evt, grid) { + if ($scope.selectionProvider.selectedItems === undefined) { + return true; + } + + var charCode = evt.which || evt.keyCode, + newColumnIndex, + lastInRow = false, + firstInRow = false, + rowIndex = $scope.selectionProvider.lastClickedRow === undefined ? 1 : $scope.selectionProvider.lastClickedRow.rowIndex, + visibleCols = $scope.columns.filter(function(c) { return c.visible; }), + pinnedCols = $scope.columns.filter(function(c) { return c.pinned; }); + + if ($scope.col) { + newColumnIndex = visibleCols.indexOf($scope.col); + } + + if (charCode !== 37 && charCode !== 38 && charCode !== 39 && charCode !== 40 && charCode !== 9 && charCode !== 13) { + return true; + } + + if ($scope.enableCellSelection) { + if (charCode === 9) { //tab key + evt.preventDefault(); + } + + var focusedOnFirstColumn = $scope.showSelectionCheckbox ? $scope.col.index === 1 : $scope.col.index === 0; + var focusedOnFirstVisibleColumns = $scope.$index === 1 || $scope.$index === 0; + var focusedOnLastVisibleColumns = $scope.$index === ($scope.renderedColumns.length - 1) || $scope.$index === ($scope.renderedColumns.length - 2); + var focusedOnLastColumn = visibleCols.indexOf($scope.col) === (visibleCols.length - 1); + var focusedOnLastPinnedColumn = pinnedCols.indexOf($scope.col) === (pinnedCols.length - 1); + + if (charCode === 37 || charCode === 9 && evt.shiftKey) { + var scrollTo = 0; + + if (!focusedOnFirstColumn) { + newColumnIndex -= 1; + } + + if (focusedOnFirstVisibleColumns) { + if (focusedOnFirstColumn && charCode === 9 && evt.shiftKey){ + scrollTo = grid.$canvas.width(); + newColumnIndex = visibleCols.length - 1; + firstInRow = true; + } + else { + scrollTo = grid.$viewport.scrollLeft() - $scope.col.width; + } + } + else if (pinnedCols.length > 0) { + scrollTo = grid.$viewport.scrollLeft() - visibleCols[newColumnIndex].width; + } + + grid.$viewport.scrollLeft(scrollTo); + + } + else if (charCode === 39 || charCode === 9 && !evt.shiftKey) { + if (focusedOnLastVisibleColumns) { + if (focusedOnLastColumn && charCode === 9 && !evt.shiftKey) { + grid.$viewport.scrollLeft(0); + newColumnIndex = $scope.showSelectionCheckbox ? 1 : 0; + lastInRow = true; + } + else { + grid.$viewport.scrollLeft(grid.$viewport.scrollLeft() + $scope.col.width); + } + } + else if (focusedOnLastPinnedColumn) { + grid.$viewport.scrollLeft(0); + } + + if (!focusedOnLastColumn) { + newColumnIndex += 1; + } + } + } + + var items; + if ($scope.configGroups.length > 0) { + items = grid.rowFactory.parsedData.filter(function (row) { + return !row.isAggRow; + }); + } + else { + items = grid.filteredRows; + } + + var offset = 0; + if (rowIndex !== 0 && (charCode === 38 || charCode === 13 && evt.shiftKey || charCode === 9 && evt.shiftKey && firstInRow)) { //arrow key up or shift enter or tab key and first item in row + offset = -1; + } + else if (rowIndex !== items.length - 1 && (charCode === 40 || charCode === 13 && !evt.shiftKey || charCode === 9 && lastInRow)) {//arrow key down, enter, or tab key and last item in row? + offset = 1; + } + + if (offset) { + var r = items[rowIndex + offset]; + if (r.beforeSelectionChange(r, evt)) { + r.continueSelection(evt); + $scope.$emit('ngGridEventDigestGridParent'); + + if ($scope.selectionProvider.lastClickedRow.renderedRowIndex >= $scope.renderedRows.length - EXCESS_ROWS - 2) { + grid.$viewport.scrollTop(grid.$viewport.scrollTop() + $scope.rowHeight); + } + else if ($scope.selectionProvider.lastClickedRow.renderedRowIndex <= EXCESS_ROWS + 2) { + grid.$viewport.scrollTop(grid.$viewport.scrollTop() - $scope.rowHeight); + } + } + } + + if ($scope.enableCellSelection) { + setTimeout(function(){ + $scope.domAccessProvider.focusCellElement($scope, $scope.renderedColumns.indexOf(visibleCols[newColumnIndex])); + }, 3); + } + + return false; +}; + +if (!String.prototype.trim) { + String.prototype.trim = function() { + return this.replace(/^\s+|\s+$/g, ''); + }; +} +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(elt /*, from*/) { + var len = this.length >>> 0; + var from = Number(arguments[1]) || 0; + from = (from < 0) ? Math.ceil(from) : Math.floor(from); + if (from < 0) { + from += len; + } + for (; from < len; from++) { + if (from in this && this[from] === elt) { + return from; + } + } + return -1; + }; +} +if (!Array.prototype.filter) { + Array.prototype.filter = function(fun /*, thisp */) { + "use strict"; + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") { + throw new TypeError(); + } + var res = []; + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; // in case fun mutates this + if (fun.call(thisp, val, i, t)) { + res.push(val); + } + } + } + return res; + }; +} +ngGridFilters.filter('checkmark', function() { + return function(input) { + return input ? '\u2714' : '\u2718'; + }; +}); +ngGridFilters.filter('ngColumns', function() { + return function(input) { + return input.filter(function(col) { + return !col.isAggCol; + }); + }; +}); +angular.module('ngGrid.services').factory('$domUtilityService',['$utilityService', function($utils) { + var domUtilityService = {}; + var regexCache = {}; + var getWidths = function() { + var $testContainer = $('<div></div>'); + $testContainer.appendTo('body'); + // 1. Run all the following measurements on startup! + //measure Scroll Bars + $testContainer.height(100).width(100).css("position", "absolute").css("overflow", "scroll"); + $testContainer.append('<div style="height: 400px; width: 400px;"></div>'); + domUtilityService.ScrollH = ($testContainer.height() - $testContainer[0].clientHeight); + domUtilityService.ScrollW = ($testContainer.width() - $testContainer[0].clientWidth); + $testContainer.empty(); + //clear styles + $testContainer.attr('style', ''); + //measure letter sizes using a pretty typical font size and fat font-family + $testContainer.append('<span style="font-family: Verdana, Helvetica, Sans-Serif; font-size: 14px;"><strong>M</strong></span>'); + domUtilityService.LetterW = $testContainer.children().first().width(); + $testContainer.remove(); + }; + domUtilityService.eventStorage = {}; + domUtilityService.AssignGridContainers = function($scope, rootEl, grid) { + grid.$root = $(rootEl); + //Headers + grid.$topPanel = grid.$root.find(".ngTopPanel"); + grid.$groupPanel = grid.$root.find(".ngGroupPanel"); + grid.$headerContainer = grid.$topPanel.find(".ngHeaderContainer"); + $scope.$headerContainer = grid.$headerContainer; + + grid.$headerScroller = grid.$topPanel.find(".ngHeaderScroller"); + grid.$headers = grid.$headerScroller.children(); + //Viewport + grid.$viewport = grid.$root.find(".ngViewport"); + //Canvas + grid.$canvas = grid.$viewport.find(".ngCanvas"); + //Footers + grid.$footerPanel = grid.$root.find(".ngFooterPanel"); + + $scope.$watch(function () { + return grid.$viewport.scrollLeft(); + }, function (newLeft) { + return grid.$headerContainer.scrollLeft(newLeft); + }); + domUtilityService.UpdateGridLayout($scope, grid); + }; + domUtilityService.getRealWidth = function (obj) { + var width = 0; + var props = { visibility: "hidden", display: "block" }; + var hiddenParents = obj.parents().andSelf().not(':visible'); + $.swap(hiddenParents[0], props, function () { + width = obj.outerWidth(); + }); + return width; + }; + domUtilityService.UpdateGridLayout = function($scope, grid) { + //catch this so we can return the viewer to their original scroll after the resize! + var scrollTop = grid.$viewport.scrollTop(); + grid.elementDims.rootMaxW = grid.$root.width(); + if (grid.$root.is(':hidden')) { + grid.elementDims.rootMaxW = domUtilityService.getRealWidth(grid.$root); + } + grid.elementDims.rootMaxH = grid.$root.height(); + //check to see if anything has changed + grid.refreshDomSizes(); + $scope.adjustScrollTop(scrollTop, true); //ensure that the user stays scrolled where they were + }; + domUtilityService.numberOfGrids = 0; + domUtilityService.BuildStyles = function($scope, grid, digest) { + var rowHeight = grid.config.rowHeight, + $style = grid.$styleSheet, + gridId = grid.gridId, + css, + cols = $scope.columns, + sumWidth = 0; + + if (!$style) { + $style = $('#' + gridId); + if (!$style[0]) { + $style = $("<style id='" + gridId + "' type='text/css' rel='stylesheet' />").appendTo(grid.$root); + } + } + $style.empty(); + var trw = $scope.totalRowWidth(); + css = "." + gridId + " .ngCanvas { width: " + trw + "px; }" + + "." + gridId + " .ngRow { width: " + trw + "px; }" + + "." + gridId + " .ngCanvas { width: " + trw + "px; }" + + "." + gridId + " .ngHeaderScroller { width: " + (trw + domUtilityService.ScrollH) + "px}"; + + for (var i = 0; i < cols.length; i++) { + var col = cols[i]; + if (col.visible !== false) { + css += "." + gridId + " .col" + i + " { width: " + col.width + "px; left: " + sumWidth + "px; height: " + rowHeight + "px }" + + "." + gridId + " .colt" + i + " { width: " + col.width + "px; }"; + sumWidth += col.width; + } + } + + if ($utils.isIe) { // IE + $style[0].styleSheet.cssText = css; + } + + else { + $style[0].appendChild(document.createTextNode(css)); + } + + grid.$styleSheet = $style; + + $scope.adjustScrollLeft(grid.$viewport.scrollLeft()); + if (digest) { + domUtilityService.digest($scope); + } + }; + domUtilityService.setColLeft = function(col, colLeft, grid) { + if (grid.$styleSheet) { + var regex = regexCache[col.index]; + if (!regex) { + regex = regexCache[col.index] = new RegExp(".col" + col.index + " { width: [0-9]+px; left: [0-9]+px"); + } + var str = grid.$styleSheet.html(); + var newStr = str.replace(regex, ".col" + col.index + " { width: " + col.width + "px; left: " + colLeft + "px"); + if ($utils.isIe) { // IE + setTimeout(function() { + grid.$styleSheet.html(newStr); + }); + } + else { + grid.$styleSheet.html(newStr); + } + } + }; + domUtilityService.setColLeft.immediate = 1; + domUtilityService.RebuildGrid = function($scope, grid){ + domUtilityService.UpdateGridLayout($scope, grid); + if (grid.config.maintainColumnRatios == null || grid.config.maintainColumnRatios) { + grid.configureColumnWidths(); + } + $scope.adjustScrollLeft(grid.$viewport.scrollLeft()); + domUtilityService.BuildStyles($scope, grid, true); + }; + + domUtilityService.digest = function($scope) { + if (!$scope.$root.$$phase) { + $scope.$digest(); + } + }; + domUtilityService.ScrollH = 17; // default in IE, Chrome, & most browsers + domUtilityService.ScrollW = 17; // default in IE, Chrome, & most browsers + domUtilityService.LetterW = 10; + getWidths(); + return domUtilityService; +}]); +angular.module('ngGrid.services').factory('$sortService', ['$parse', function($parse) { + var sortService = {}; + sortService.colSortFnCache = {}; // cache of sorting functions. Once we create them, we don't want to keep re-doing it + // this takes an piece of data from the cell and tries to determine its type and what sorting + // function to use for it + // @item - the cell data + sortService.guessSortFn = function(item) { + var itemType = typeof(item); + //check for numbers and booleans + switch (itemType) { + case "number": + return sortService.sortNumber; + case "boolean": + return sortService.sortBool; + case "string": + // if number string return number string sort fn. else return the str + return item.match(/^[-+]?[£$¤]?[\d,.]+%?$/) ? sortService.sortNumberStr : sortService.sortAlpha; + default: + //check if the item is a valid Date + if (Object.prototype.toString.call(item) === '[object Date]') { + return sortService.sortDate; + } + else { + //finally just sort the basic sort... + return sortService.basicSort; + } + } + }; + //#region Sorting Functions + sortService.basicSort = function(a, b) { + if (a === b) { + return 0; + } + if (a < b) { + return -1; + } + return 1; + }; + sortService.sortNumber = function(a, b) { + return a - b; + }; + sortService.sortNumberStr = function(a, b) { + var numA, numB, badA = false, badB = false; + numA = parseFloat(a.replace(/[^0-9.-]/g, '')); + if (isNaN(numA)) { + badA = true; + } + numB = parseFloat(b.replace(/[^0-9.-]/g, '')); + if (isNaN(numB)) { + badB = true; + } + // we want bad ones to get pushed to the bottom... which effectively is "greater than" + if (badA && badB) { + return 0; + } + if (badA) { + return 1; + } + if (badB) { + return -1; + } + return numA - numB; + }; + sortService.sortAlpha = function(a, b) { + var strA = a.toLowerCase(), + strB = b.toLowerCase(); + return strA === strB ? 0 : (strA < strB ? -1 : 1); + }; + sortService.sortDate = function(a, b) { + var timeA = a.getTime(), + timeB = b.getTime(); + return timeA === timeB ? 0 : (timeA < timeB ? -1 : 1); + }; + sortService.sortBool = function(a, b) { + if (a && b) { + return 0; + } + if (!a && !b) { + return 0; + } else { + return a ? 1 : -1; + } + }; + //#endregion + // the core sorting logic trigger + sortService.sortData = function(sortInfo, data /*datasource*/) { + // first make sure we are even supposed to do work + if (!data || !sortInfo) { + return; + } + var l = sortInfo.fields.length, + order = sortInfo.fields, + col, + direction, + // IE9 HACK.... omg, I can't reference data array within the sort fn below. has to be a separate reference....!!!! + d = data.slice(0); + //now actually sort the data + data.sort(function (itemA, itemB) { + var tem = 0, + indx = 0, + sortFn; + while (tem === 0 && indx < l) { + // grab the metadata for the rest of the logic + col = sortInfo.columns[indx]; + direction = sortInfo.directions[indx]; + sortFn = sortService.getSortFn(col, d); + + var propA = $parse(order[indx])(itemA); + var propB = $parse(order[indx])(itemB); + // we want to allow zero values to be evaluated in the sort function + if ((!propA && propA !== 0) || (!propB && propB !== 0)) { + // we want to force nulls and such to the bottom when we sort... which effectively is "greater than" + if (!propB && !propA) { + tem = 0; + } + else if (!propA) { + tem = 1; + } + else if (!propB) { + tem = -1; + } + } + else { + tem = sortFn(propA, propB); + } + indx++; + } + //made it this far, we don't have to worry about null & undefined + if (direction === ASC) { + return tem; + } else { + return 0 - tem; + } + }); + }; + sortService.Sort = function(sortInfo, data) { + if (sortService.isSorting) { + return; + } + sortService.isSorting = true; + sortService.sortData(sortInfo, data); + sortService.isSorting = false; + }; + sortService.getSortFn = function(col, data) { + var sortFn, item; + //see if we already figured out what to use to sort the column + if (sortService.colSortFnCache[col.field]) { + sortFn = sortService.colSortFnCache[col.field]; + } + else if (col.sortingAlgorithm !== undefined) { + sortFn = col.sortingAlgorithm; + sortService.colSortFnCache[col.field] = col.sortingAlgorithm; + } + else { // try and guess what sort function to use + item = data[0]; + if (!item) { + return sortFn; + } + sortFn = sortService.guessSortFn($parse(col.field)(item)); + //cache it + if (sortFn) { + sortService.colSortFnCache[col.field] = sortFn; + } else { + // we assign the alpha sort because anything that is null/undefined will never get passed to + // the actual sorting function. It will get caught in our null check and returned to be sorted + // down to the bottom + sortFn = sortService.sortAlpha; + } + } + return sortFn; + }; + return sortService; +}]); + +angular.module('ngGrid.services').factory('$utilityService', ['$parse', function ($parse) { + var funcNameRegex = /function (.{1,})\(/; + var utils = { + visualLength: function(node) { + var elem = document.getElementById('testDataLength'); + if (!elem) { + elem = document.createElement('SPAN'); + elem.id = "testDataLength"; + elem.style.visibility = "hidden"; + document.body.appendChild(elem); + } + $(elem).css('font', $(node).css('font')); + $(elem).css('font-size', $(node).css('font-size')); + $(elem).css('font-family', $(node).css('font-family')); + elem.innerHTML = $(node).text(); + return elem.offsetWidth; + }, + forIn: function(obj, action) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + action(obj[prop], prop); + } + } + }, + evalProperty: function (entity, path) { + return $parse(path)(entity); + }, + endsWith: function(str, suffix) { + if (!str || !suffix || typeof str !== "string") { + return false; + } + return str.indexOf(suffix, str.length - suffix.length) !== -1; + }, + isNullOrUndefined: function(obj) { + if (obj === undefined || obj === null) { + return true; + } + return false; + }, + getElementsByClassName: function(cl) { + var retnode = []; + var myclass = new RegExp('\\b' + cl + '\\b'); + var elem = document.getElementsByTagName('*'); + for (var i = 0; i < elem.length; i++) { + var classes = elem[i].className; + if (myclass.test(classes)) { + retnode.push(elem[i]); + } + } + return retnode; + }, + newId: (function() { + var seedId = new Date().getTime(); + return function() { + return seedId += 1; + }; + })(), + seti18n: function($scope, language) { + var $langPack = window.ngGrid.i18n[language]; + for (var label in $langPack) { + $scope.i18n[label] = $langPack[label]; + } + }, + getInstanceType: function (o) { + var results = (funcNameRegex).exec(o.constructor.toString()); + if (results && results.length > 1) { + var instanceType = results[1].replace(/^\s+|\s+$/g, ""); // Trim surrounding whitespace; IE appears to add a space at the end + return instanceType; + } + else { + return ""; + } + }, + // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness) + // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10. + // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser. + // If there is a future need to detect specific versions of IE10+, we will amend this. + ieVersion: (function() { + var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i'); + + // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment + do{ + div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->'; + }while(iElems[0]); + return version > 4 ? version : undefined; + })() + }; + + $.extend(utils, { + isIe: (function() { + return utils.ieVersion !== undefined; + })() + }); + return utils; +}]); + +var ngAggregate = function (aggEntity, rowFactory, rowHeight, groupInitState) { + this.rowIndex = 0; + this.offsetTop = this.rowIndex * rowHeight; + this.entity = aggEntity; + this.label = aggEntity.gLabel; + this.field = aggEntity.gField; + this.depth = aggEntity.gDepth; + this.parent = aggEntity.parent; + this.children = aggEntity.children; + this.aggChildren = aggEntity.aggChildren; + this.aggIndex = aggEntity.aggIndex; + this.collapsed = groupInitState; + this.groupInitState = groupInitState; + this.rowFactory = rowFactory; + this.rowHeight = rowHeight; + this.isAggRow = true; + this.offsetLeft = aggEntity.gDepth * 25; + this.aggLabelFilter = aggEntity.aggLabelFilter; +}; + +ngAggregate.prototype.toggleExpand = function () { + this.collapsed = this.collapsed ? false : true; + if (this.orig) { + this.orig.collapsed = this.collapsed; + } + this.notifyChildren(); +}; +ngAggregate.prototype.setExpand = function (state) { + this.collapsed = state; + this.notifyChildren(); +}; +ngAggregate.prototype.notifyChildren = function () { + var longest = Math.max(this.rowFactory.aggCache.length, this.children.length); + for (var i = 0; i < longest; i++) { + if (this.aggChildren[i]) { + this.aggChildren[i].entity[NG_HIDDEN] = this.collapsed; + if (this.collapsed) { + this.aggChildren[i].setExpand(this.collapsed); + } + } + if (this.children[i]) { + this.children[i][NG_HIDDEN] = this.collapsed; + } + if (i > this.aggIndex && this.rowFactory.aggCache[i]) { + var agg = this.rowFactory.aggCache[i]; + var offset = (30 * this.children.length); + agg.offsetTop = this.collapsed ? agg.offsetTop - offset : agg.offsetTop + offset; + } + } + this.rowFactory.renderedChange(); +}; +ngAggregate.prototype.aggClass = function () { + return this.collapsed ? "ngAggArrowCollapsed" : "ngAggArrowExpanded"; +}; +ngAggregate.prototype.totalChildren = function () { + if (this.aggChildren.length > 0) { + var i = 0; + var recurse = function (cur) { + if (cur.aggChildren.length > 0) { + angular.forEach(cur.aggChildren, function (a) { + recurse(a); + }); + } else { + i += cur.children.length; + } + }; + recurse(this); + return i; + } else { + return this.children.length; + } +}; +ngAggregate.prototype.copy = function () { + var ret = new ngAggregate(this.entity, this.rowFactory, this.rowHeight, this.groupInitState); + ret.orig = this; + return ret; +}; +var ngColumn = function (config, $scope, grid, domUtilityService, $templateCache, $utils) { + var self = this, + colDef = config.colDef, + delay = 500, + clicks = 0, + timer = null; + self.colDef = config.colDef; + self.width = colDef.width; + self.groupIndex = 0; + self.isGroupedBy = false; + self.minWidth = !colDef.minWidth ? 50 : colDef.minWidth; + self.maxWidth = !colDef.maxWidth ? 9000 : colDef.maxWidth; + + // TODO: Use the column's definition for enabling cell editing + // self.enableCellEdit = config.enableCellEdit || colDef.enableCellEdit; + self.enableCellEdit = colDef.enableCellEdit !== undefined ? colDef.enableCellEdit : (config.enableCellEdit || config.enableCellEditOnFocus); + + self.headerRowHeight = config.headerRowHeight; + + // Use colDef.displayName as long as it's not undefined, otherwise default to the field name + self.displayName = (colDef.displayName === undefined) ? colDef.field : colDef.displayName; + + self.index = config.index; + self.isAggCol = config.isAggCol; + self.cellClass = colDef.cellClass; + self.sortPriority = undefined; + self.cellFilter = colDef.cellFilter ? colDef.cellFilter : ""; + self.field = colDef.field; + self.aggLabelFilter = colDef.cellFilter || colDef.aggLabelFilter; + self.visible = $utils.isNullOrUndefined(colDef.visible) || colDef.visible; + self.sortable = false; + self.resizable = false; + self.pinnable = false; + self.pinned = (config.enablePinning && colDef.pinned); + self.originalIndex = config.originalIndex == null ? self.index : config.originalIndex; + self.groupable = $utils.isNullOrUndefined(colDef.groupable) || colDef.groupable; + if (config.enableSort) { + self.sortable = $utils.isNullOrUndefined(colDef.sortable) || colDef.sortable; + } + if (config.enableResize) { + self.resizable = $utils.isNullOrUndefined(colDef.resizable) || colDef.resizable; + } + if (config.enablePinning) { + self.pinnable = $utils.isNullOrUndefined(colDef.pinnable) || colDef.pinnable; + } + self.sortDirection = undefined; + self.sortingAlgorithm = colDef.sortFn; + self.headerClass = colDef.headerClass; + self.cursor = self.sortable ? 'pointer' : 'default'; + self.headerCellTemplate = colDef.headerCellTemplate || $templateCache.get('headerCellTemplate.html'); + self.cellTemplate = colDef.cellTemplate || $templateCache.get('cellTemplate.html').replace(CUSTOM_FILTERS, self.cellFilter ? "|" + self.cellFilter : ""); + if(self.enableCellEdit) { + self.cellEditTemplate = $templateCache.get('cellEditTemplate.html'); + self.editableCellTemplate = colDef.editableCellTemplate || $templateCache.get('editableCellTemplate.html'); + } + if (colDef.cellTemplate && !TEMPLATE_REGEXP.test(colDef.cellTemplate)) { + self.cellTemplate = $.ajax({ + type: "GET", + url: colDef.cellTemplate, + async: false + }).responseText; + } + if (self.enableCellEdit && colDef.editableCellTemplate && !TEMPLATE_REGEXP.test(colDef.editableCellTemplate)) { + self.editableCellTemplate = $.ajax({ + type: "GET", + url: colDef.editableCellTemplate, + async: false + }).responseText; + } + if (colDef.headerCellTemplate && !TEMPLATE_REGEXP.test(colDef.headerCellTemplate)) { + self.headerCellTemplate = $.ajax({ + type: "GET", + url: colDef.headerCellTemplate, + async: false + }).responseText; + } + self.colIndex = function () { + var classes = self.pinned ? "pinned " : ""; + classes += "col" + self.index + " colt" + self.index; + if (self.cellClass) { + classes += " " + self.cellClass; + } + return classes; + }; + self.groupedByClass = function() { + return self.isGroupedBy ? "ngGroupedByIcon" : "ngGroupIcon"; + }; + self.toggleVisible = function() { + self.visible = !self.visible; + }; + self.showSortButtonUp = function() { + return self.sortable ? self.sortDirection === DESC : self.sortable; + }; + self.showSortButtonDown = function() { + return self.sortable ? self.sortDirection === ASC : self.sortable; + }; + self.noSortVisible = function() { + return !self.sortDirection; + }; + self.sort = function(evt) { + if (!self.sortable) { + return true; // column sorting is disabled, do nothing + } + var dir = self.sortDirection === ASC ? DESC : ASC; + self.sortDirection = dir; + config.sortCallback(self, evt); + return false; + }; + self.gripClick = function() { + clicks++; //count clicks + if (clicks === 1) { + timer = setTimeout(function() { + //Here you can add a single click action. + clicks = 0; //after action performed, reset counter + }, delay); + } else { + clearTimeout(timer); //prevent single-click action + config.resizeOnDataCallback(self); //perform double-click action + clicks = 0; //after action performed, reset counter + } + }; + self.gripOnMouseDown = function(event) { + $scope.isColumnResizing = true; + if (event.ctrlKey && !self.pinned) { + self.toggleVisible(); + domUtilityService.BuildStyles($scope, grid); + return true; + } + event.target.parentElement.style.cursor = 'col-resize'; + self.startMousePosition = event.clientX; + self.origWidth = self.width; + $(document).mousemove(self.onMouseMove); + $(document).mouseup(self.gripOnMouseUp); + return false; + }; + self.onMouseMove = function(event) { + var diff = event.clientX - self.startMousePosition; + var newWidth = diff + self.origWidth; + self.width = (newWidth < self.minWidth ? self.minWidth : (newWidth > self.maxWidth ? self.maxWidth : newWidth)); + $scope.hasUserChangedGridColumnWidths = true; + domUtilityService.BuildStyles($scope, grid); + return false; + }; + self.gripOnMouseUp = function (event) { + $(document).off('mousemove', self.onMouseMove); + $(document).off('mouseup', self.gripOnMouseUp); + event.target.parentElement.style.cursor = 'default'; + domUtilityService.digest($scope); + $scope.isColumnResizing = false; + return false; + }; + self.copy = function() { + var ret = new ngColumn(config, $scope, grid, domUtilityService, $templateCache); + ret.isClone = true; + ret.orig = self; + return ret; + }; + self.setVars = function (fromCol) { + self.orig = fromCol; + self.width = fromCol.width; + self.groupIndex = fromCol.groupIndex; + self.isGroupedBy = fromCol.isGroupedBy; + self.displayName = fromCol.displayName; + self.index = fromCol.index; + self.isAggCol = fromCol.isAggCol; + self.cellClass = fromCol.cellClass; + self.cellFilter = fromCol.cellFilter; + self.field = fromCol.field; + self.aggLabelFilter = fromCol.aggLabelFilter; + self.visible = fromCol.visible; + self.sortable = fromCol.sortable; + self.resizable = fromCol.resizable; + self.pinnable = fromCol.pinnable; + self.pinned = fromCol.pinned; + self.originalIndex = fromCol.originalIndex; + self.sortDirection = fromCol.sortDirection; + self.sortingAlgorithm = fromCol.sortingAlgorithm; + self.headerClass = fromCol.headerClass; + self.headerCellTemplate = fromCol.headerCellTemplate; + self.cellTemplate = fromCol.cellTemplate; + self.cellEditTemplate = fromCol.cellEditTemplate; + }; +}; + +var ngDimension = function (options) { + this.outerHeight = null; + this.outerWidth = null; + $.extend(this, options); +}; +var ngDomAccessProvider = function (grid) { + this.previousColumn = null; + this.grid = grid; + +}; + +ngDomAccessProvider.prototype.changeUserSelect = function (elm, value) { + elm.css({ + '-webkit-touch-callout': value, + '-webkit-user-select': value, + '-khtml-user-select': value, + '-moz-user-select': value === 'none' ? '-moz-none' : value, + '-ms-user-select': value, + 'user-select': value + }); +}; +ngDomAccessProvider.prototype.focusCellElement = function ($scope, index) { + if ($scope.selectionProvider.lastClickedRow) { + var columnIndex = index !== undefined ? index : this.previousColumn; + var elm = $scope.selectionProvider.lastClickedRow.clone ? $scope.selectionProvider.lastClickedRow.clone.elm : $scope.selectionProvider.lastClickedRow.elm; + if (columnIndex !== undefined && elm) { + var columns = angular.element(elm[0].children).filter(function () { return this.nodeType !== 8; }); //Remove html comments for IE8 + var i = Math.max(Math.min($scope.renderedColumns.length - 1, columnIndex), 0); + if (this.grid.config.showSelectionCheckbox && angular.element(columns[i]).scope() && angular.element(columns[i]).scope().col.index === 0) { + i = 1; //don't want to focus on checkbox + } + if (columns[i]) { + columns[i].children[1].children[0].focus(); + } + this.previousColumn = columnIndex; + } + } +}; +ngDomAccessProvider.prototype.selectionHandlers = function ($scope, elm) { + var doingKeyDown = false; + var self = this; + elm.bind('keydown', function (evt) { + if (evt.keyCode === 16) { //shift key + self.changeUserSelect(elm, 'none', evt); + return true; + } else if (!doingKeyDown) { + doingKeyDown = true; + var ret = ngMoveSelectionHandler($scope, elm, evt, self.grid); + doingKeyDown = false; + return ret; + } + return true; + }); + elm.bind('keyup', function (evt) { + if (evt.keyCode === 16) { //shift key + self.changeUserSelect(elm, 'text', evt); + } + return true; + }); +}; +var ngEventProvider = function (grid, $scope, domUtilityService, $timeout) { + var self = this; + // The init method gets called during the ng-grid directive execution. + self.colToMove = undefined; + self.groupToMove = undefined; + self.assignEvents = function() { + // Here we set the onmousedown event handler to the header container. + if (grid.config.jqueryUIDraggable && !grid.config.enablePinning) { + grid.$groupPanel.droppable({ + addClasses: false, + drop: function(event) { + self.onGroupDrop(event); + } + }); + } else { + grid.$groupPanel.on('mousedown', self.onGroupMouseDown).on('dragover', self.dragOver).on('drop', self.onGroupDrop); + grid.$headerScroller.on('mousedown', self.onHeaderMouseDown).on('dragover', self.dragOver); + if (grid.config.enableColumnReordering && !grid.config.enablePinning) { + grid.$headerScroller.on('drop', self.onHeaderDrop); + } + } + $scope.$watch('renderedColumns', function() { + $timeout(self.setDraggables); + }); + }; + self.dragStart = function(evt){ + //FireFox requires there to be dataTransfer if you want to drag and drop. + evt.dataTransfer.setData('text', ''); //cannot be empty string + }; + self.dragOver = function(evt) { + evt.preventDefault(); + }; + //For JQueryUI + self.setDraggables = function() { + if (!grid.config.jqueryUIDraggable) { + //Fix for FireFox. Instead of using jQuery on('dragstart', function) on find, we have to use addEventListeners for each column. + var columns = grid.$root.find('.ngHeaderSortColumn'); //have to iterate if using addEventListener + angular.forEach(columns, function(col){ + if(col.className && col.className.indexOf("ngHeaderSortColumn") !== -1){ + col.setAttribute('draggable', 'true'); + //jQuery 'on' function doesn't have dataTransfer as part of event in handler unless added to event props, which is not recommended + //See more here: http://api.jquery.com/category/events/event-object/ + if (col.addEventListener) { //IE8 doesn't have drag drop or event listeners + col.addEventListener('dragstart', self.dragStart); + } + } + }); + if (navigator.userAgent.indexOf("MSIE") !== -1){ + //call native IE dragDrop() to start dragging + grid.$root.find('.ngHeaderSortColumn').bind('selectstart', function () { + this.dragDrop(); + return false; + }); + } + } else { + grid.$root.find('.ngHeaderSortColumn').draggable({ + helper: 'clone', + appendTo: 'body', + stack: 'div', + addClasses: false, + start: function(event) { + self.onHeaderMouseDown(event); + } + }).droppable({ + drop: function(event) { + self.onHeaderDrop(event); + } + }); + } + }; + self.onGroupMouseDown = function(event) { + var groupItem = $(event.target); + // Get the scope from the header container + if (groupItem[0].className !== 'ngRemoveGroup') { + var groupItemScope = angular.element(groupItem).scope(); + if (groupItemScope) { + // set draggable events + if (!grid.config.jqueryUIDraggable) { + groupItem.attr('draggable', 'true'); + if(this.addEventListener){//IE8 doesn't have drag drop or event listeners + this.addEventListener('dragstart', self.dragStart); + } + if (navigator.userAgent.indexOf("MSIE") !== -1){ + //call native IE dragDrop() to start dragging + groupItem.bind('selectstart', function () { + this.dragDrop(); + return false; + }); + } + } + // Save the column for later. + self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index }; + } + } else { + self.groupToMove = undefined; + } + }; + self.onGroupDrop = function(event) { + event.stopPropagation(); + // clear out the colToMove object + var groupContainer; + var groupScope; + if (self.groupToMove) { + // Get the closest header to where we dropped + groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header. + if (groupContainer.context.className === 'ngGroupPanel') { + $scope.configGroups.splice(self.groupToMove.index, 1); + $scope.configGroups.push(self.groupToMove.groupName); + } else { + groupScope = angular.element(groupContainer).scope(); + if (groupScope) { + // If we have the same column, do nothing. + if (self.groupToMove.index !== groupScope.$index) { + // Splice the columns + $scope.configGroups.splice(self.groupToMove.index, 1); + $scope.configGroups.splice(groupScope.$index, 0, self.groupToMove.groupName); + } + } + } + self.groupToMove = undefined; + grid.fixGroupIndexes(); + } else if (self.colToMove) { + if ($scope.configGroups.indexOf(self.colToMove.col) === -1) { + groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header. + if (groupContainer.context.className === 'ngGroupPanel' || groupContainer.context.className === 'ngGroupPanelDescription ng-binding') { + $scope.groupBy(self.colToMove.col); + } else { + groupScope = angular.element(groupContainer).scope(); + if (groupScope) { + // Splice the columns + $scope.removeGroup(groupScope.$index); + } + } + } + self.colToMove = undefined; + } + if (!$scope.$$phase) { + $scope.$apply(); + } + }; + //Header functions + self.onHeaderMouseDown = function(event) { + // Get the closest header container from where we clicked. + var headerContainer = $(event.target).closest('.ngHeaderSortColumn'); + // Get the scope from the header container + var headerScope = angular.element(headerContainer).scope(); + if (headerScope) { + // Save the column for later. + self.colToMove = { header: headerContainer, col: headerScope.col }; + } + }; + self.onHeaderDrop = function(event) { + if (!self.colToMove || self.colToMove.col.pinned) { + return; + } + // Get the closest header to where we dropped + var headerContainer = $(event.target).closest('.ngHeaderSortColumn'); + // Get the scope from the header. + var headerScope = angular.element(headerContainer).scope(); + if (headerScope) { + // If we have the same column, do nothing. + if (self.colToMove.col === headerScope.col) { + return; + } + // Splice the columns + $scope.columns.splice(self.colToMove.col.index, 1); + $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col); + grid.fixColumnIndexes(); + // clear out the colToMove object + self.colToMove = undefined; + domUtilityService.digest($scope); + } + }; + + self.assignGridEventHandlers = function() { + //Chrome and firefox both need a tab index so the grid can recieve focus. + //need to give the grid a tabindex if it doesn't already have one so + //we'll just give it a tab index of the corresponding gridcache index + //that way we'll get the same result every time it is run. + //configurable within the options. + if (grid.config.tabIndex === -1) { + grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids); + domUtilityService.numberOfGrids++; + } else { + grid.$viewport.attr('tabIndex', grid.config.tabIndex); + } + // resize on window resize + var windowThrottle; + $(window).resize(function(){ + clearTimeout(windowThrottle); + windowThrottle = setTimeout(function() { + //in function for IE8 compatibility + domUtilityService.RebuildGrid($scope,grid); + }, 100); + }); + // resize on parent resize as well. + var parentThrottle; + $(grid.$root.parent()).on('resize', function() { + clearTimeout(parentThrottle); + parentThrottle = setTimeout(function() { + //in function for IE8 compatibility + domUtilityService.RebuildGrid($scope,grid); + }, 100); + }); + }; + // In this example we want to assign grid events. + self.assignGridEventHandlers(); + self.assignEvents(); +}; + +var ngFooter = function ($scope, grid) { + $scope.maxRows = function () { + var ret = Math.max($scope.totalServerItems, grid.data.length); + return ret; + }; + + $scope.multiSelect = (grid.config.enableRowSelection && grid.config.multiSelect); + $scope.selectedItemCount = grid.selectedItemCount; + $scope.maxPages = function () { + return Math.ceil($scope.maxRows() / $scope.pagingOptions.pageSize); + }; + + $scope.pageForward = function() { + var page = $scope.pagingOptions.currentPage; + if ($scope.totalServerItems > 0) { + $scope.pagingOptions.currentPage = Math.min(page + 1, $scope.maxPages()); + } else { + $scope.pagingOptions.currentPage++; + } + }; + + $scope.pageBackward = function() { + var page = $scope.pagingOptions.currentPage; + $scope.pagingOptions.currentPage = Math.max(page - 1, 1); + }; + + $scope.pageToFirst = function() { + $scope.pagingOptions.currentPage = 1; + }; + + $scope.pageToLast = function() { + var maxPages = $scope.maxPages(); + $scope.pagingOptions.currentPage = maxPages; + }; + + $scope.cantPageForward = function() { + var curPage = $scope.pagingOptions.currentPage; + var maxPages = $scope.maxPages(); + if ($scope.totalServerItems > 0) { + return curPage >= maxPages; + } else { + return grid.data.length < 1; + } + + }; + $scope.cantPageToLast = function() { + if ($scope.totalServerItems > 0) { + return $scope.cantPageForward(); + } else { + return true; + } + }; + + $scope.cantPageBackward = function() { + var curPage = $scope.pagingOptions.currentPage; + return curPage <= 1; + }; +}; +/// <reference path="footer.js" /> +/// <reference path="../services/SortService.js" /> +/// <reference path="../../lib/jquery-1.8.2.min" /> +var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) { + var defaults = { + //Define an aggregate template to customize the rows when grouped. See github wiki for more details. + aggregateTemplate: undefined, + + //Callback for when you want to validate something after selection. + afterSelectionChange: function() { + }, + + /* Callback if you want to inspect something before selection, + return false if you want to cancel the selection. return true otherwise. + If you need to wait for an async call to proceed with selection you can + use rowItem.changeSelection(event) method after returning false initially. + Note: when shift+ Selecting multiple items in the grid this will only get called + once and the rowItem will be an array of items that are queued to be selected. */ + beforeSelectionChange: function() { + return true; + }, + + //checkbox templates. + checkboxCellTemplate: undefined, + checkboxHeaderTemplate: undefined, + + //definitions of columns as an array [], if not defines columns are auto-generated. See github wiki for more details. + columnDefs: undefined, + + //*Data being displayed in the grid. Each item in the array is mapped to a row being displayed. + data: [], + + //Data updated callback, fires every time the data is modified from outside the grid. + dataUpdated: function() { + }, + + //Enables cell editing. + enableCellEdit: false, + + //Enables cell editing on focus + enableCellEditOnFocus: false, + + //Enables cell selection. + enableCellSelection: false, + + //Enable or disable resizing of columns + enableColumnResize: false, + + //Enable or disable reordering of columns + enableColumnReordering: false, + + //Enable or disable HEAVY column virtualization. This turns off selection checkboxes and column pinning and is designed for spreadsheet-like data. + enableColumnHeavyVirt: false, + + //Enables the server-side paging feature + enablePaging: false, + + //Enable column pinning + enablePinning: false, + + //To be able to have selectable rows in grid. + enableRowSelection: true, + + //Enables or disables sorting in grid. + enableSorting: true, + + //Enables or disables text highlighting in grid by adding the "unselectable" class (See CSS file) + enableHighlighting: false, + + // string list of properties to exclude when auto-generating columns. + excludeProperties: [], + + /* filterOptions - + filterText: The text bound to the built-in search box. + useExternalFilter: Bypass internal filtering if you want to roll your own filtering mechanism but want to use builtin search box. + */ + filterOptions: { + filterText: "", + useExternalFilter: false + }, + + //Defining the height of the footer in pixels. + footerRowHeight: 55, + + // the template for the column menu and filter, including the button. + footerTemplate: undefined, + + //Initial fields to group data by. Array of field names, not displayName. + groups: [], + + // set the initial state of aggreagate grouping. "true" means they will be collapsed when grouping changes, "false" means they will be expanded by default. + groupsCollapsedByDefault: true, + + //The height of the header row in pixels. + headerRowHeight: 30, + + //Define a header row template for further customization. See github wiki for more details. + headerRowTemplate: undefined, + + /*Enables the use of jquery UI reaggable/droppable plugin. requires jqueryUI to work if enabled. + Useful if you want drag + drop but your users insist on crappy browsers. */ + jqueryUIDraggable: false, + + //Enable the use jqueryUIThemes + jqueryUITheme: false, + + //Prevent unselections when in single selection mode. + keepLastSelected: true, + + /*Maintains the column widths while resizing. + Defaults to true when using *'s or undefined widths. Can be ovverriden by setting to false.*/ + maintainColumnRatios: undefined, + + // the template for the column menu and filter, including the button. + menuTemplate: undefined, + + //Set this to false if you only want one item selected at a time + multiSelect: true, + + // pagingOptions - + pagingOptions: { + // pageSizes: list of available page sizes. + pageSizes: [250, 500, 1000], + //pageSize: currently selected page size. + pageSize: 250, + //currentPage: the uhm... current page. + currentPage: 1 + }, + + //the selection checkbox is pinned to the left side of the viewport or not. + pinSelectionCheckbox: false, + + //Array of plugin functions to register in ng-grid + plugins: [], + + //User defined unique ID field that allows for better handling of selections and for server-side paging + primaryKey: undefined, + + //Row height of rows in grid. + rowHeight: 30, + + //Define a row template to customize output. See github wiki for more details. + rowTemplate: undefined, + + //all of the items selected in the grid. In single select mode there will only be one item in the array. + selectedItems: [], + + //Disable row selections by clicking on the row and only when the checkbox is clicked. + selectWithCheckboxOnly: false, + + /*Enables menu to choose which columns to display and group by. + If both showColumnMenu and showFilter are false the menu button will not display.*/ + showColumnMenu: false, + + /*Enables display of the filterbox in the column menu. + If both showColumnMenu and showFilter are false the menu button will not display.*/ + showFilter: false, + + //Show or hide the footer alltogether the footer is enabled by default + showFooter: false, + + //Show the dropzone for drag and drop grouping + showGroupPanel: false, + + //Row selection check boxes appear as the first column. + showSelectionCheckbox: false, + + /*Define a sortInfo object to specify a default sorting state. + You can also observe this variable to utilize server-side sorting (see useExternalSorting). + Syntax is sortinfo: { fields: ['fieldName1',' fieldName2'], direction: 'ASC'/'asc' || 'desc'/'DESC'}*/ + sortInfo: {fields: [], columns: [], directions: [] }, + + //Set the tab index of the Vieport. + tabIndex: -1, + + //totalServerItems: Total items are on the server. + totalServerItems: 0, + + /*Prevents the internal sorting from executing. + The sortInfo object will be updated with the sorting information so you can handle sorting (see sortInfo)*/ + useExternalSorting: false, + + /*i18n language support. choose from the installed or included languages, en, fr, sp, etc...*/ + i18n: 'en', + + //the threshold in rows to force virtualization on + virtualizationThreshold: 50 + }, + self = this; + self.maxCanvasHt = 0; + //self vars + self.config = $.extend(defaults, window.ngGrid.config, options); + + // override conflicting settings + self.config.showSelectionCheckbox = (self.config.showSelectionCheckbox && self.config.enableColumnHeavyVirt === false); + self.config.enablePinning = (self.config.enablePinning && self.config.enableColumnHeavyVirt === false); + self.config.selectWithCheckboxOnly = (self.config.selectWithCheckboxOnly && self.config.showSelectionCheckbox !== false); + self.config.pinSelectionCheckbox = self.config.enablePinning; + + if (typeof options.columnDefs === "string") { + self.config.columnDefs = $scope.$eval(options.columnDefs); + } + self.rowCache = []; + self.rowMap = []; + self.gridId = "ng" + $utils.newId(); + self.$root = null; //this is the root element that is passed in with the binding handler + self.$groupPanel = null; + self.$topPanel = null; + self.$headerContainer = null; + self.$headerScroller = null; + self.$headers = null; + self.$viewport = null; + self.$canvas = null; + self.rootDim = self.config.gridDim; + self.data = []; + self.lateBindColumns = false; + self.filteredRows = []; + + self.initTemplates = function() { + var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate']; + + var promises = []; + angular.forEach(templates, function(template) { + promises.push( self.getTemplate(template) ); + }); + + return $q.all(promises); + }; + + //Templates + // test templates for urls and get the tempaltes via synchronous ajax calls + self.getTemplate = function (key) { + var t = self.config[key]; + var uKey = self.gridId + key + ".html"; + var p = $q.defer(); + if (t && !TEMPLATE_REGEXP.test(t)) { + $http.get(t, { + cache: $templateCache + }) + .success(function(data){ + $templateCache.put(uKey, data); + p.resolve(); + }) + .error(function(err){ + p.reject("Could not load template: " + t); + }); + } else if (t) { + $templateCache.put(uKey, t); + p.resolve(); + } else { + var dKey = key + ".html"; + $templateCache.put(uKey, $templateCache.get(dKey)); + p.resolve(); + } + + return p.promise; + }; + + if (typeof self.config.data === "object") { + self.data = self.config.data; // we cannot watch for updates if you don't pass the string name + } + self.calcMaxCanvasHeight = function() { + var calculatedHeight; + if(self.config.groups.length > 0){ + calculatedHeight = self.rowFactory.parsedData.filter(function(e) { + return !e[NG_HIDDEN]; + }).length * self.config.rowHeight; + } else { + calculatedHeight = self.filteredRows.length * self.config.rowHeight; + } + return calculatedHeight; + }; + self.elementDims = { + scrollW: 0, + scrollH: 0, + rowIndexCellW: 25, + rowSelectedCellW: 25, + rootMaxW: 0, + rootMaxH: 0 + }; + //self funcs + self.setRenderedRows = function (newRows) { + $scope.renderedRows.length = newRows.length; + for (var i = 0; i < newRows.length; i++) { + if (!$scope.renderedRows[i] || (newRows[i].isAggRow || $scope.renderedRows[i].isAggRow)) { + $scope.renderedRows[i] = newRows[i].copy(); + $scope.renderedRows[i].collapsed = newRows[i].collapsed; + if (!newRows[i].isAggRow) { + $scope.renderedRows[i].setVars(newRows[i]); + } + } else { + $scope.renderedRows[i].setVars(newRows[i]); + } + $scope.renderedRows[i].rowIndex = newRows[i].rowIndex; + $scope.renderedRows[i].offsetTop = newRows[i].offsetTop; + $scope.renderedRows[i].selected = newRows[i].selected; + newRows[i].renderedRowIndex = i; + } + self.refreshDomSizes(); + $scope.$emit('ngGridEventRows', newRows); + }; + self.minRowsToRender = function() { + var viewportH = $scope.viewportDimHeight() || 1; + return Math.floor(viewportH / self.config.rowHeight); + }; + self.refreshDomSizes = function() { + var dim = new ngDimension(); + dim.outerWidth = self.elementDims.rootMaxW; + dim.outerHeight = self.elementDims.rootMaxH; + self.rootDim = dim; + self.maxCanvasHt = self.calcMaxCanvasHeight(); + }; + self.buildColumnDefsFromData = function () { + self.config.columnDefsFromData = true; + self.config.columnDefs = []; + var item = self.data[0]; + if (!item) { + self.lateBoundColumns = true; + return; + } + $utils.forIn(item, function (prop, propName) { + if (self.config.excludeProperties.indexOf(propName) === -1) { + self.config.columnDefs.push({ + field: propName + }); + } + }); + }; + self.buildColumns = function() { + var columnDefs = self.config.columnDefs, + cols = []; + if (!columnDefs) { + self.buildColumnDefsFromData(); + columnDefs = self.config.columnDefs; + } + if (self.config.showSelectionCheckbox) { + cols.push(new ngColumn({ + colDef: { + field: '\u2714', + width: self.elementDims.rowSelectedCellW, + sortable: false, + resizable: false, + groupable: false, + headerCellTemplate: $templateCache.get($scope.gridId + 'checkboxHeaderTemplate.html'), + cellTemplate: $templateCache.get($scope.gridId + 'checkboxCellTemplate.html'), + pinned: self.config.pinSelectionCheckbox + }, + index: 0, + headerRowHeight: self.config.headerRowHeight, + sortCallback: self.sortData, + resizeOnDataCallback: self.resizeOnData, + enableResize: self.config.enableColumnResize, + enableSort: self.config.enableSorting, + enablePinning: self.config.enablePinning + }, $scope, self, domUtilityService, $templateCache, $utils)); + } + if (columnDefs.length > 0) { + var checkboxOffset = self.config.showSelectionCheckbox ? 1 : 0; + var groupOffset = $scope.configGroups.length; + $scope.configGroups.length = 0; + angular.forEach(columnDefs, function(colDef, i) { + i += checkboxOffset; + var column = new ngColumn({ + colDef: colDef, + index: i + groupOffset, + originalIndex: i, + headerRowHeight: self.config.headerRowHeight, + sortCallback: self.sortData, + resizeOnDataCallback: self.resizeOnData, + enableResize: self.config.enableColumnResize, + enableSort: self.config.enableSorting, + enablePinning: self.config.enablePinning, + enableCellEdit: self.config.enableCellEdit || self.config.enableCellEditOnFocus + }, $scope, self, domUtilityService, $templateCache, $utils); + var indx = self.config.groups.indexOf(colDef.field); + if (indx !== -1) { + column.isGroupedBy = true; + $scope.configGroups.splice(indx, 0, column); + column.groupIndex = $scope.configGroups.length; + } + cols.push(column); + }); + $scope.columns = cols; + if ($scope.configGroups.length > 0) { + self.rowFactory.getGrouping($scope.configGroups); + } + } + }; + self.configureColumnWidths = function() { + var asterisksArray = [], + percentArray = [], + asteriskNum = 0, + totalWidth = 0; + + // When rearranging columns, their index in $scope.columns will no longer match the original column order from columnDefs causing + // their width config to be out of sync. We can use "originalIndex" on the ngColumns to get hold of the correct setup from columnDefs, but to + // avoid O(n) lookups in $scope.columns per column we setup a map. + var indexMap = {}; + // Build a map of columnDefs column indices -> ngColumn indices (via the "originalIndex" property on ngColumns). + angular.forEach($scope.columns, function(ngCol, i) { + // Disregard columns created by grouping (the grouping columns don't match a column from columnDefs) + if (!$utils.isNullOrUndefined(ngCol.originalIndex)) { + var origIndex = ngCol.originalIndex; + if (self.config.showSelectionCheckbox) { + //if visible, takes up 25 pixels + if(ngCol.originalIndex === 0 && ngCol.visible){ + totalWidth += 25; + } + // The originalIndex will be offset 1 when including the selection column + origIndex--; + } + indexMap[origIndex] = i; + } + }); + + angular.forEach(self.config.columnDefs, function(colDef, i) { + // Get the ngColumn that matches the current column from columnDefs + var ngColumn = $scope.columns[indexMap[i]]; + + colDef.index = i; + + var isPercent = false, t; + //if width is not defined, set it to a single star + if ($utils.isNullOrUndefined(colDef.width)) { + colDef.width = "*"; + } else { // get column width + isPercent = isNaN(colDef.width) ? $utils.endsWith(colDef.width, "%") : false; + t = isPercent ? colDef.width : parseInt(colDef.width, 10); + } + + // check if it is a number + if (isNaN(t) && !$scope.hasUserChangedGridColumnWidths) { + t = colDef.width; + // figure out if the width is defined or if we need to calculate it + if (t === 'auto') { // set it for now until we have data and subscribe when it changes so we can set the width. + ngColumn.width = ngColumn.minWidth; + totalWidth += ngColumn.width; + var temp = ngColumn; + + $scope.$on("ngGridEventData", function () { + self.resizeOnData(temp); + }); + return; + } else if (t.indexOf("*") !== -1) { // we need to save it until the end to do the calulations on the remaining width. + if (ngColumn.visible !== false) { + asteriskNum += t.length; + } + asterisksArray.push(colDef); + return; + } else if (isPercent) { // If the width is a percentage, save it until the very last. + percentArray.push(colDef); + return; + } else { // we can't parse the width so lets throw an error. + throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid"; + } + } else if (ngColumn.visible !== false) { + totalWidth += ngColumn.width = parseInt(ngColumn.width, 10); + } + }); + + // Now we check if we saved any percentage columns for calculating last + if (percentArray.length > 0) { + //If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true. + self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false; + // If any columns with % widths have been hidden, then let other % based columns use their width + var percentWidth = 0; // The total % value for all columns setting their width using % (will e.g. be 40 for 2 columns with 20% each) + var hiddenPercent = 0; // The total % value for all columns setting their width using %, but which have been hidden + angular.forEach(percentArray, function(colDef) { + // Get the ngColumn that matches the current column from columnDefs + var ngColumn = $scope.columns[indexMap[colDef.index]]; + var t = colDef.width; + var percent = parseInt(t.slice(0, -1), 10) / 100; + percentWidth += percent; + + if (!ngColumn.visible) { + hiddenPercent += percent; + } + }); + var percentWidthUsed = percentWidth - hiddenPercent; + + // do the math + angular.forEach(percentArray, function(colDef) { + // Get the ngColumn that matches the current column from columnDefs + var ngColumn = $scope.columns[indexMap[colDef.index]]; + + // Calc the % relative to the amount of % reserved for the visible columns (that use % based widths) + var t = colDef.width; + var percent = parseInt(t.slice(0, -1), 10) / 100; + if (hiddenPercent > 0) { + percent = percent / percentWidthUsed; + } + else { + percent = percent / percentWidth; + } + + var pixelsForPercentBasedWidth = self.rootDim.outerWidth * percentWidth; + ngColumn.width = Math.floor(pixelsForPercentBasedWidth * percent); + totalWidth += ngColumn.width; + }); + } + + // check if we saved any asterisk columns for calculating later + if (asterisksArray.length > 0) { + //If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true. + self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false; + // get the remaining width + var remainingWidth = self.rootDim.outerWidth - totalWidth; + // are we overflowing vertically? + if (self.maxCanvasHt > $scope.viewportDimHeight()) { + //compensate for scrollbar + remainingWidth -= domUtilityService.ScrollW; + } + // calculate the weight of each asterisk rounded down + var asteriskVal = Math.floor(remainingWidth / asteriskNum); + + // set the width of each column based on the number of stars + angular.forEach(asterisksArray, function(colDef, i) { + // Get the ngColumn that matches the current column from columnDefs + var ngColumn = $scope.columns[indexMap[colDef.index]]; + ngColumn.width = asteriskVal * colDef.width.length; + if (ngColumn.visible !== false) { + totalWidth += ngColumn.width; + } + + var isLast = (i === (asterisksArray.length - 1)); + //if last asterisk and doesn't fill width of grid, add the difference + if(isLast && totalWidth < self.rootDim.outerWidth){ + var gridWidthDifference = self.rootDim.outerWidth - totalWidth; + if(self.maxCanvasHt > $scope.viewportDimHeight()){ + gridWidthDifference -= domUtilityService.ScrollW; + } + ngColumn.width += gridWidthDifference; + } + }); + } + }; + self.init = function() { + return self.initTemplates().then(function(){ + //factories and services + $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); + $scope.domAccessProvider = new ngDomAccessProvider(self); + self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); + self.searchProvider = new ngSearchProvider($scope, self, $filter); + self.styleProvider = new ngStyleProvider($scope, self); + $scope.$watch('configGroups', function(a) { + var tempArr = []; + angular.forEach(a, function(item) { + tempArr.push(item.field || item); + }); + self.config.groups = tempArr; + self.rowFactory.filteredRowsChanged(); + $scope.$emit('ngGridEventGroups', a); + }, true); + $scope.$watch('columns', function (a) { + if(!$scope.isColumnResizing){ + domUtilityService.RebuildGrid($scope, self); + } + $scope.$emit('ngGridEventColumns', a); + }, true); + $scope.$watch(function() { + return options.i18n; + }, function(newLang) { + $utils.seti18n($scope, newLang); + }); + self.maxCanvasHt = self.calcMaxCanvasHeight(); + + if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { + $scope.$watch(function() { + return self.config.sortInfo; + }, function(sortInfo){ + if (!sortService.isSorting) { + self.sortColumnsInit(); + $scope.$emit('ngGridEventSorted', self.config.sortInfo); + } + },true); + } + }); + + // var p = $q.defer(); + // p.resolve(); + // return p.promise; + }; + + self.resizeOnData = function(col) { + // we calculate the longest data. + var longest = col.minWidth; + var arr = $utils.getElementsByClassName('col' + col.index); + angular.forEach(arr, function(elem, index) { + var i; + if (index === 0) { + var kgHeaderText = $(elem).find('.ngHeaderText'); + i = $utils.visualLength(kgHeaderText) + 10; // +10 some margin + } else { + var ngCellText = $(elem).find('.ngCellText'); + i = $utils.visualLength(ngCellText) + 10; // +10 some margin + } + if (i > longest) { + longest = i; + } + }); + col.width = col.longest = Math.min(col.maxWidth, longest + 7); // + 7 px to make it look decent. + domUtilityService.BuildStyles($scope, self, true); + }; + self.lastSortedColumns = []; + self.sortData = function(col, evt) { + if (evt && evt.shiftKey && self.config.sortInfo) { + var indx = self.config.sortInfo.columns.indexOf(col); + if (indx === -1) { + if (self.config.sortInfo.columns.length === 1) { + self.config.sortInfo.columns[0].sortPriority = 1; + } + self.config.sortInfo.columns.push(col); + col.sortPriority = self.config.sortInfo.columns.length; + self.config.sortInfo.fields.push(col.field); + self.config.sortInfo.directions.push(col.sortDirection); + self.lastSortedColumns.push(col); + } else { + self.config.sortInfo.directions[indx] = col.sortDirection; + } + } else { + var isArr = $.isArray(col); + self.config.sortInfo.columns.length = 0; + self.config.sortInfo.fields.length = 0; + self.config.sortInfo.directions.length = 0; + var push = function (c) { + self.config.sortInfo.columns.push(c); + self.config.sortInfo.fields.push(c.field); + self.config.sortInfo.directions.push(c.sortDirection); + self.lastSortedColumns.push(c); + }; + if (isArr) { + self.clearSortingData(); + angular.forEach(col, function (c, i) { + c.sortPriority = i + 1; + push(c); + }); + } else { + self.clearSortingData(col); + col.sortPriority = undefined; + push(col); + } + } + self.sortActual(); + self.searchProvider.evalFilter(); + $scope.$emit('ngGridEventSorted', self.config.sortInfo); + }; + self.sortColumnsInit = function() { + if (self.config.sortInfo.columns) { + self.config.sortInfo.columns.length = 0; + } else { + self.config.sortInfo.columns = []; + } + angular.forEach($scope.columns, function(c) { + var i = self.config.sortInfo.fields.indexOf(c.field); + if (i !== -1) { + c.sortDirection = self.config.sortInfo.directions[i] || 'asc'; + self.config.sortInfo.columns[i] = c; + } + }); + angular.forEach(self.config.sortInfo.columns, function(c){ + self.sortData(c); + }); + }; + self.sortActual = function() { + if (!self.config.useExternalSorting) { + var tempData = self.data.slice(0); + angular.forEach(tempData, function(item, i) { + var e = self.rowMap[i]; + if (e !== undefined) { + var v = self.rowCache[e]; + if (v !== undefined) { + item.preSortSelected = v.selected; + item.preSortIndex = i; + } + } + }); + sortService.Sort(self.config.sortInfo, tempData); + angular.forEach(tempData, function(item, i) { + self.rowCache[i].entity = item; + self.rowCache[i].selected = item.preSortSelected; + self.rowMap[item.preSortIndex] = i; + delete item.preSortSelected; + delete item.preSortIndex; + }); + } + }; + + self.clearSortingData = function (col) { + if (!col) { + angular.forEach(self.lastSortedColumns, function (c) { + c.sortDirection = ""; + c.sortPriority = null; + }); + self.lastSortedColumns = []; + } else { + angular.forEach(self.lastSortedColumns, function (c) { + if (col.index !== c.index) { + c.sortDirection = ""; + c.sortPriority = null; + } + }); + self.lastSortedColumns[0] = col; + self.lastSortedColumns.length = 1; + } + }; + self.fixColumnIndexes = function() { + //fix column indexes + for (var i = 0; i < $scope.columns.length; i++) { + $scope.columns[i].index = i; + } + }; + self.fixGroupIndexes = function() { + angular.forEach($scope.configGroups, function(item, i) { + item.groupIndex = i + 1; + }); + }; + //$scope vars + $scope.elementsNeedMeasuring = true; + $scope.columns = []; + $scope.renderedRows = []; + $scope.renderedColumns = []; + $scope.headerRow = null; + $scope.rowHeight = self.config.rowHeight; + $scope.jqueryUITheme = self.config.jqueryUITheme; + $scope.showSelectionCheckbox = self.config.showSelectionCheckbox; + $scope.enableCellSelection = self.config.enableCellSelection; + $scope.enableCellEditOnFocus = self.config.enableCellEditOnFocus; + $scope.footer = null; + $scope.selectedItems = self.config.selectedItems; + $scope.multiSelect = self.config.multiSelect; + $scope.showFooter = self.config.showFooter; + $scope.footerRowHeight = $scope.showFooter ? self.config.footerRowHeight : 0; + $scope.showColumnMenu = self.config.showColumnMenu; + $scope.showMenu = false; + $scope.configGroups = []; + $scope.gridId = self.gridId; + //Paging + $scope.enablePaging = self.config.enablePaging; + $scope.pagingOptions = self.config.pagingOptions; + + //i18n support + $scope.i18n = {}; + $utils.seti18n($scope, self.config.i18n); + $scope.adjustScrollLeft = function (scrollLeft) { + var colwidths = 0, + totalLeft = 0, + x = $scope.columns.length, + newCols = [], + dcv = !self.config.enableColumnHeavyVirt; + var r = 0; + var addCol = function (c) { + if (dcv) { + newCols.push(c); + } else { + if (!$scope.renderedColumns[r]) { + $scope.renderedColumns[r] = c.copy(); + } else { + $scope.renderedColumns[r].setVars(c); + } + } + r++; + }; + for (var i = 0; i < x; i++) { + var col = $scope.columns[i]; + if (col.visible !== false) { + var w = col.width + colwidths; + if (col.pinned) { + addCol(col); + var newLeft = i > 0 ? (scrollLeft + totalLeft) : scrollLeft; + domUtilityService.setColLeft(col, newLeft, self); + totalLeft += col.width; + } else { + if (w >= scrollLeft) { + if (colwidths <= scrollLeft + self.rootDim.outerWidth) { + addCol(col); + } + } + } + colwidths += col.width; + } + } + if (dcv) { + $scope.renderedColumns = newCols; + } + }; + self.prevScrollTop = 0; + self.prevScrollIndex = 0; + $scope.adjustScrollTop = function(scrollTop, force) { + if (self.prevScrollTop === scrollTop && !force) { + return; + } + if (scrollTop > 0 && self.$viewport[0].scrollHeight - scrollTop <= self.$viewport.outerHeight()) { + $scope.$emit('ngGridEventScroll'); + } + var rowIndex = Math.floor(scrollTop / self.config.rowHeight); + var newRange; + if (self.filteredRows.length > self.config.virtualizationThreshold) { + // Have we hit the threshold going down? + if (self.prevScrollTop < scrollTop && rowIndex < self.prevScrollIndex + SCROLL_THRESHOLD) { + return; + } + //Have we hit the threshold going up? + if (self.prevScrollTop > scrollTop && rowIndex > self.prevScrollIndex - SCROLL_THRESHOLD) { + return; + } + newRange = new ngRange(Math.max(0, rowIndex - EXCESS_ROWS), rowIndex + self.minRowsToRender() + EXCESS_ROWS); + } else { + var maxLen = $scope.configGroups.length > 0 ? self.rowFactory.parsedData.length : self.data.length; + newRange = new ngRange(0, Math.max(maxLen, self.minRowsToRender() + EXCESS_ROWS)); + } + self.prevScrollTop = scrollTop; + self.rowFactory.UpdateViewableRange(newRange); + self.prevScrollIndex = rowIndex; + }; + + //scope funcs + $scope.toggleShowMenu = function() { + $scope.showMenu = !$scope.showMenu; + }; + $scope.toggleSelectAll = function(state, selectOnlyVisible) { + $scope.selectionProvider.toggleSelectAll(state, false, selectOnlyVisible); + }; + $scope.totalFilteredItemsLength = function() { + return self.filteredRows.length; + }; + $scope.showGroupPanel = function() { + return self.config.showGroupPanel; + }; + $scope.topPanelHeight = function() { + return self.config.showGroupPanel === true ? self.config.headerRowHeight + 32 : self.config.headerRowHeight; + }; + + $scope.viewportDimHeight = function() { + return Math.max(0, self.rootDim.outerHeight - $scope.topPanelHeight() - $scope.footerRowHeight - 2); + }; + $scope.groupBy = function (col) { + if (self.data.length < 1 || !col.groupable || !col.field) { + return; + } + //first sort the column + if (!col.sortDirection) { + col.sort({ shiftKey: $scope.configGroups.length > 0 ? true : false }); + } + + var indx = $scope.configGroups.indexOf(col); + if (indx === -1) { + col.isGroupedBy = true; + $scope.configGroups.push(col); + col.groupIndex = $scope.configGroups.length; + } else { + $scope.removeGroup(indx); + } + self.$viewport.scrollTop(0); + domUtilityService.digest($scope); + }; + $scope.removeGroup = function(index) { + var col = $scope.columns.filter(function(item) { + return item.groupIndex === (index + 1); + })[0]; + col.isGroupedBy = false; + col.groupIndex = 0; + if ($scope.columns[index].isAggCol) { + $scope.columns.splice(index, 1); + $scope.configGroups.splice(index, 1); + self.fixGroupIndexes(); + } + if ($scope.configGroups.length === 0) { + self.fixColumnIndexes(); + domUtilityService.digest($scope); + } + $scope.adjustScrollLeft(0); + }; + $scope.togglePin = function (col) { + var indexFrom = col.index; + var indexTo = 0; + for (var i = 0; i < $scope.columns.length; i++) { + if (!$scope.columns[i].pinned) { + break; + } + indexTo++; + } + if (col.pinned) { + indexTo = Math.max(col.originalIndex, indexTo - 1); + } + col.pinned = !col.pinned; + // Splice the columns + $scope.columns.splice(indexFrom, 1); + $scope.columns.splice(indexTo, 0, col); + self.fixColumnIndexes(); + // Finally, rebuild the CSS styles. + domUtilityService.BuildStyles($scope, self, true); + self.$viewport.scrollLeft(self.$viewport.scrollLeft() - col.width); + }; + $scope.totalRowWidth = function() { + var totalWidth = 0, + cols = $scope.columns; + for (var i = 0; i < cols.length; i++) { + if (cols[i].visible !== false) { + totalWidth += cols[i].width; + } + } + return totalWidth; + }; + $scope.headerScrollerDim = function() { + var viewportH = $scope.viewportDimHeight(), + maxHeight = self.maxCanvasHt, + vScrollBarIsOpen = (maxHeight > viewportH), + newDim = new ngDimension(); + + newDim.autoFitHeight = true; + newDim.outerWidth = $scope.totalRowWidth(); + if (vScrollBarIsOpen) { + newDim.outerWidth += self.elementDims.scrollW; + } else if ((maxHeight - viewportH) <= self.elementDims.scrollH) { //if the horizontal scroll is open it forces the viewport to be smaller + newDim.outerWidth += self.elementDims.scrollW; + } + return newDim; + }; +}; + +var ngRange = function (top, bottom) { + this.topRow = top; + this.bottomRow = bottom; +}; +var ngRow = function (entity, config, selectionProvider, rowIndex, $utils) { + this.entity = entity; + this.config = config; + this.selectionProvider = selectionProvider; + this.rowIndex = rowIndex; + this.utils = $utils; + this.selected = selectionProvider.getSelection(entity); + this.cursor = this.config.enableRowSelection ? 'pointer' : 'default'; + this.beforeSelectionChange = config.beforeSelectionChangeCallback; + this.afterSelectionChange = config.afterSelectionChangeCallback; + this.offsetTop = this.rowIndex * config.rowHeight; + this.rowDisplayIndex = 0; +}; + +ngRow.prototype.setSelection = function (isSelected) { + this.selectionProvider.setSelection(this, isSelected); + this.selectionProvider.lastClickedRow = this; +}; +ngRow.prototype.continueSelection = function (event) { + this.selectionProvider.ChangeSelection(this, event); +}; +ngRow.prototype.ensureEntity = function (expected) { + if (this.entity !== expected) { + // Update the entity and determine our selected property + this.entity = expected; + this.selected = this.selectionProvider.getSelection(this.entity); + } +}; +ngRow.prototype.toggleSelected = function (event) { + if (!this.config.enableRowSelection && !this.config.enableCellSelection) { + return true; + } + var element = event.target || event; + //check and make sure its not the bubbling up of our checked 'click' event + if (element.type === "checkbox" && element.parentElement.className !== "ngSelectionCell ng-scope") { + return true; + } + if (this.config.selectWithCheckboxOnly && element.type !== "checkbox") { + this.selectionProvider.lastClickedRow = this; + return true; + } + if (this.beforeSelectionChange(this, event)) { + this.continueSelection(event); + } + return false; +}; +ngRow.prototype.alternatingRowClass = function () { + var isEven = (this.rowIndex % 2) === 0; + var classes = { + 'ngRow' : true, + 'selected': this.selected, + 'even': isEven, + 'odd': !isEven, + 'ui-state-default': this.config.jqueryUITheme && isEven, + 'ui-state-active': this.config.jqueryUITheme && !isEven + }; + return classes; +}; +ngRow.prototype.getProperty = function (path) { + return this.utils.evalProperty(this.entity, path); +}; +ngRow.prototype.copy = function () { + this.clone = new ngRow(this.entity, this.config, this.selectionProvider, this.rowIndex, this.utils); + this.clone.isClone = true; + this.clone.elm = this.elm; + this.clone.orig = this; + return this.clone; +}; +ngRow.prototype.setVars = function (fromRow) { + fromRow.clone = this; + this.entity = fromRow.entity; + this.selected = fromRow.selected; + this.orig = fromRow; +}; +var ngRowFactory = function (grid, $scope, domUtilityService, $templateCache, $utils) { + var self = this; + // we cache rows when they are built, and then blow the cache away when sorting + self.aggCache = {}; + self.parentCache = []; // Used for grouping and is cleared each time groups are calulated. + self.dataChanged = true; + self.parsedData = []; + self.rowConfig = {}; + self.selectionProvider = $scope.selectionProvider; + self.rowHeight = 30; + self.numberOfAggregates = 0; + self.groupedData = undefined; + self.rowHeight = grid.config.rowHeight; + self.rowConfig = { + enableRowSelection: grid.config.enableRowSelection, + rowClasses: grid.config.rowClasses, + selectedItems: $scope.selectedItems, + selectWithCheckboxOnly: grid.config.selectWithCheckboxOnly, + beforeSelectionChangeCallback: grid.config.beforeSelectionChange, + afterSelectionChangeCallback: grid.config.afterSelectionChange, + jqueryUITheme: grid.config.jqueryUITheme, + enableCellSelection: grid.config.enableCellSelection, + rowHeight: grid.config.rowHeight + }; + + self.renderedRange = new ngRange(0, grid.minRowsToRender() + EXCESS_ROWS); + + // @entity - the data item + // @rowIndex - the index of the row + self.buildEntityRow = function(entity, rowIndex) { + // build the row + return new ngRow(entity, self.rowConfig, self.selectionProvider, rowIndex, $utils); + }; + + self.buildAggregateRow = function(aggEntity, rowIndex) { + var agg = self.aggCache[aggEntity.aggIndex]; // first check to see if we've already built it + if (!agg) { + // build the row + agg = new ngAggregate(aggEntity, self, self.rowConfig.rowHeight, grid.config.groupsCollapsedByDefault); + self.aggCache[aggEntity.aggIndex] = agg; + } + agg.rowIndex = rowIndex; + agg.offsetTop = rowIndex * self.rowConfig.rowHeight; + return agg; + }; + self.UpdateViewableRange = function(newRange) { + self.renderedRange = newRange; + self.renderedChange(); + }; + self.filteredRowsChanged = function() { + // check for latebound autogenerated columns + if (grid.lateBoundColumns && grid.filteredRows.length > 0) { + grid.config.columnDefs = undefined; + grid.buildColumns(); + grid.lateBoundColumns = false; + $scope.$evalAsync(function() { + $scope.adjustScrollLeft(0); + }); + } + self.dataChanged = true; + if (grid.config.groups.length > 0) { + self.getGrouping(grid.config.groups); + } + self.UpdateViewableRange(self.renderedRange); + }; + + self.renderedChange = function() { + if (!self.groupedData || grid.config.groups.length < 1) { + self.renderedChangeNoGroups(); + grid.refreshDomSizes(); + return; + } + self.wasGrouped = true; + self.parentCache = []; + var x = 0; + var temp = self.parsedData.filter(function (e) { + if (e.isAggRow) { + if (e.parent && e.parent.collapsed) { + return false; + } + return true; + } + if (!e[NG_HIDDEN]) { + e.rowIndex = x++; + } + return !e[NG_HIDDEN]; + }); + self.totalRows = temp.length; + var rowArr = []; + for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) { + if (temp[i]) { + temp[i].offsetTop = i * grid.config.rowHeight; + rowArr.push(temp[i]); + } + } + grid.setRenderedRows(rowArr); + }; + + self.renderedChangeNoGroups = function () { + var rowArr = []; + for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) { + if (grid.filteredRows[i]) { + grid.filteredRows[i].rowIndex = i; + grid.filteredRows[i].offsetTop = i * grid.config.rowHeight; + rowArr.push(grid.filteredRows[i]); + } + } + grid.setRenderedRows(rowArr); + }; + + self.fixRowCache = function () { + var newLen = grid.data.length; + var diff = newLen - grid.rowCache.length; + if (diff < 0) { + grid.rowCache.length = grid.rowMap.length = newLen; + } else { + for (var i = grid.rowCache.length; i < newLen; i++) { + grid.rowCache[i] = grid.rowFactory.buildEntityRow(grid.data[i], i); + } + } + }; + + //magical recursion. it works. I swear it. I figured it out in the shower one day. + self.parseGroupData = function(g) { + if (g.values) { + for (var x = 0; x < g.values.length; x++){ + // get the last parent in the array because that's where our children want to be + self.parentCache[self.parentCache.length - 1].children.push(g.values[x]); + //add the row to our return array + self.parsedData.push(g.values[x]); + } + } else { + for (var prop in g) { + // exclude the meta properties. + if (prop === NG_FIELD || prop === NG_DEPTH || prop === NG_COLUMN) { + continue; + } else if (g.hasOwnProperty(prop)) { + //build the aggregate row + var agg = self.buildAggregateRow({ + gField: g[NG_FIELD], + gLabel: prop, + gDepth: g[NG_DEPTH], + isAggRow: true, + '_ng_hidden_': false, + children: [], + aggChildren: [], + aggIndex: self.numberOfAggregates, + aggLabelFilter: g[NG_COLUMN].aggLabelFilter + }, 0); + self.numberOfAggregates++; + //set the aggregate parent to the parent in the array that is one less deep. + agg.parent = self.parentCache[agg.depth - 1]; + // if we have a parent, set the parent to not be collapsed and append the current agg to its children + if (agg.parent) { + agg.parent.collapsed = false; + agg.parent.aggChildren.push(agg); + } + // add the aggregate row to the parsed data. + self.parsedData.push(agg); + // the current aggregate now the parent of the current depth + self.parentCache[agg.depth] = agg; + // dig deeper for more aggregates or children. + self.parseGroupData(g[prop]); + } + } + } + }; + //Shuffle the data into their respective groupings. + self.getGrouping = function(groups) { + self.aggCache = []; + self.numberOfAggregates = 0; + self.groupedData = {}; + // Here we set the onmousedown event handler to the header container. + var rows = grid.filteredRows, + maxDepth = groups.length, + cols = $scope.columns; + + function filterCols(cols, group) { + return cols.filter(function(c) { + return c.field === group; + }); + } + + for (var x = 0; x < rows.length; x++) { + var model = rows[x].entity; + if (!model) { + return; + } + rows[x][NG_HIDDEN] = grid.config.groupsCollapsedByDefault; + var ptr = self.groupedData; + + for (var y = 0; y < groups.length; y++) { + var group = groups[y]; + + var col = filterCols(cols, group)[0]; + + var val = $utils.evalProperty(model, group); + val = val ? val.toString() : 'null'; + if (!ptr[val]) { + ptr[val] = {}; + } + if (!ptr[NG_FIELD]) { + ptr[NG_FIELD] = group; + } + if (!ptr[NG_DEPTH]) { + ptr[NG_DEPTH] = y; + } + if (!ptr[NG_COLUMN]) { + ptr[NG_COLUMN] = col; + } + ptr = ptr[val]; + } + if (!ptr.values) { + ptr.values = []; + } + ptr.values.push(rows[x]); + } + + //moved out of above loops due to if no data initially, but has initial grouping, columns won't be added + for (var z = 0; z < groups.length; z++) { + if (!cols[z].isAggCol && z <= maxDepth) { + cols.splice(0, 0, new ngColumn({ + colDef: { + field: '', + width: 25, + sortable: false, + resizable: false, + headerCellTemplate: '<div class="ngAggHeader"></div>', + pinned: grid.config.pinSelectionCheckbox + + }, + enablePinning: grid.config.enablePinning, + isAggCol: true, + headerRowHeight: grid.config.headerRowHeight + + }, $scope, grid, domUtilityService, $templateCache, $utils)); + } + } + + grid.fixColumnIndexes(); + $scope.adjustScrollLeft(0); + self.parsedData.length = 0; + self.parseGroupData(self.groupedData); + self.fixRowCache(); + }; + + if (grid.config.groups.length > 0 && grid.filteredRows.length > 0) { + self.getGrouping(grid.config.groups); + } +}; +var ngSearchProvider = function ($scope, grid, $filter) { + var self = this, + searchConditions = []; + + self.extFilter = grid.config.filterOptions.useExternalFilter; + $scope.showFilter = grid.config.showFilter; + $scope.filterText = ''; + + self.fieldMap = {}; + + self.evalFilter = function () { + var filterFunc = function(item) { + for (var x = 0, len = searchConditions.length; x < len; x++) { + var condition = searchConditions[x]; + //Search entire row + var result; + if (!condition.column) { + for (var prop in item) { + if (item.hasOwnProperty(prop)) { + var c = self.fieldMap[prop.toLowerCase()]; + if (!c) { + continue; + } + var f = null, + s = null; + if (c && c.cellFilter) { + s = c.cellFilter.split(':'); + f = $filter(s[0]); + } + var pVal = item[prop]; + if (pVal !== null && pVal !== undefined) { + if (typeof f === "function") { + var filterRes = f(typeof pVal === 'object' ? evalObject(pVal, c.field) : pVal, s[1]).toString(); + result = condition.regex.test(filterRes); + } else { + result = condition.regex.test(typeof pVal === 'object' ? evalObject(pVal, c.field).toString() : pVal.toString()); + } + if (result) { + return true; + } + } + } + } + return false; + } + //Search by column. + var col = self.fieldMap[condition.columnDisplay]; + if (!col) { + return false; + } + var sp = col.cellFilter.split(':'); + var filter = col.cellFilter ? $filter(sp[0]) : null; + var value = item[condition.column] || item[col.field.split('.')[0]]; + if (value === null || value === undefined) { + return false; + } + if (typeof filter === "function") { + var filterResults = filter(typeof value === "object" ? evalObject(value, col.field) : value, sp[1]).toString(); + result = condition.regex.test(filterResults); + } + else { + result = condition.regex.test(typeof value === "object" ? evalObject(value, col.field).toString() : value.toString()); + } + if (!result) { + return false; + } + } + return true; + }; + if (searchConditions.length === 0) { + grid.filteredRows = grid.rowCache; + } else { + grid.filteredRows = grid.rowCache.filter(function(row) { + return filterFunc(row.entity); + }); + } + for (var i = 0; i < grid.filteredRows.length; i++) + { + grid.filteredRows[i].rowIndex = i; + + } + grid.rowFactory.filteredRowsChanged(); + }; + + //Traversing through the object to find the value that we want. If fail, then return the original object. + var evalObject = function (obj, columnName) { + if (typeof obj !== "object" || typeof columnName !== "string") { + return obj; + } + var args = columnName.split('.'); + var cObj = obj; + if (args.length > 1) { + for (var i = 1, len = args.length; i < len; i++) { + cObj = cObj[args[i]]; + if (!cObj) { + return obj; + } + } + return cObj; + } + return obj; + }; + var getRegExp = function (str, modifiers) { + try { + return new RegExp(str, modifiers); + } catch (err) { + //Escape all RegExp metacharacters. + return new RegExp(str.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g, '\\$1')); + } + }; + var buildSearchConditions = function (a) { + //reset. + searchConditions = []; + var qStr; + if (!(qStr = $.trim(a))) { + return; + } + var columnFilters = qStr.split(";"); + for (var i = 0; i < columnFilters.length; i++) { + var args = columnFilters[i].split(':'); + if (args.length > 1) { + var columnName = $.trim(args[0]); + var columnValue = $.trim(args[1]); + if (columnName && columnValue) { + searchConditions.push({ + column: columnName, + columnDisplay: columnName.replace(/\s+/g, '').toLowerCase(), + regex: getRegExp(columnValue, 'i') + }); + } + } else { + var val = $.trim(args[0]); + if (val) { + searchConditions.push({ + column: '', + regex: getRegExp(val, 'i') + }); + } + } + } + }; + + if (!self.extFilter) { + $scope.$watch('columns', function (cs) { + for (var i = 0; i < cs.length; i++) { + var col = cs[i]; + if (col.field) { + self.fieldMap[col.field.split('.')[0].toLowerCase()] = col; + } + if (col.displayName) { + self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g, '')] = col; + } + } + }); + } + + $scope.$watch( + function () { + return grid.config.filterOptions.filterText; + }, + function (a) { + $scope.filterText = a; + } + ); + + $scope.$watch('filterText', function(a){ + if (!self.extFilter) { + $scope.$emit('ngGridEventFilter', a); + buildSearchConditions(a); + self.evalFilter(); + } + }); +}; +var ngSelectionProvider = function (grid, $scope, $parse) { + var self = this; + self.multi = grid.config.multiSelect; + self.selectedItems = grid.config.selectedItems; + self.selectedIndex = grid.config.selectedIndex; + self.lastClickedRow = undefined; + self.ignoreSelectedItemChanges = false; // flag to prevent circular event loops keeping single-select var in sync + self.pKeyParser = $parse(grid.config.primaryKey); + + // function to manage the selection action of a data item (entity) + self.ChangeSelection = function (rowItem, evt) { + // ctrl-click + shift-click multi-selections + // up/down key navigation in multi-selections + var charCode = evt.which || evt.keyCode; + var isUpDownKeyPress = (charCode === 40 || charCode === 38); + + if (evt && evt.shiftKey && !evt.keyCode && self.multi && grid.config.enableRowSelection) { + if (self.lastClickedRow) { + var rowsArr; + if ($scope.configGroups.length > 0) { + rowsArr = grid.rowFactory.parsedData.filter(function(row) { + return !row.isAggRow; + }); + } + else { + rowsArr = grid.filteredRows; + } + + var thisIndx = rowItem.rowIndex; + var prevIndx = self.lastClickedRowIndex; + + if (thisIndx === prevIndx) { + return false; + } + + if (thisIndx < prevIndx) { + thisIndx = thisIndx ^ prevIndx; + prevIndx = thisIndx ^ prevIndx; + thisIndx = thisIndx ^ prevIndx; + thisIndx--; + } + else { + prevIndx++; + } + + var rows = []; + for (; prevIndx <= thisIndx; prevIndx++) { + rows.push(rowsArr[prevIndx]); + } + + if (rows[rows.length - 1].beforeSelectionChange(rows, evt)) { + for (var i = 0; i < rows.length; i++) { + var ri = rows[i]; + var selectionState = ri.selected; + ri.selected = !selectionState; + if (ri.clone) { + ri.clone.selected = ri.selected; + } + var index = self.selectedItems.indexOf(ri.entity); + if (index === -1) { + self.selectedItems.push(ri.entity); + } + else { + self.selectedItems.splice(index, 1); + } + } + rows[rows.length - 1].afterSelectionChange(rows, evt); + } + self.lastClickedRow = rowItem; + self.lastClickedRowIndex = rowItem.rowIndex; + + return true; + } + } + else if (!self.multi) { + if (self.lastClickedRow === rowItem) { + self.setSelection(self.lastClickedRow, grid.config.keepLastSelected ? true : !rowItem.selected); + } else { + if (self.lastClickedRow) { + self.setSelection(self.lastClickedRow, false); + } + self.setSelection(rowItem, !rowItem.selected); + } + } + else if (!evt.keyCode || isUpDownKeyPress && !grid.config.selectWithCheckboxOnly) { + self.setSelection(rowItem, !rowItem.selected); + } + self.lastClickedRow = rowItem; + self.lastClickedRowIndex = rowItem.rowIndex; + return true; + }; + + self.getSelection = function (entity) { + var isSelected = false; + if (grid.config.primaryKey) { + var val = self.pKeyParser(entity); + angular.forEach(self.selectedItems, function (c) { + if (val === self.pKeyParser(c)) { + isSelected = true; + } + }); + } + else { + isSelected = self.selectedItems.indexOf(entity) !== -1; + } + return isSelected; + }; + + // just call this func and hand it the rowItem you want to select (or de-select) + self.setSelection = function (rowItem, isSelected) { + if(grid.config.enableRowSelection){ + if (!isSelected) { + var indx = self.selectedItems.indexOf(rowItem.entity); + if (indx !== -1) { + self.selectedItems.splice(indx, 1); + } + } + else { + if (self.selectedItems.indexOf(rowItem.entity) === -1) { + if (!self.multi && self.selectedItems.length > 0) { + self.toggleSelectAll(false, true); + } + self.selectedItems.push(rowItem.entity); + } + } + rowItem.selected = isSelected; + if (rowItem.orig) { + rowItem.orig.selected = isSelected; + } + if (rowItem.clone) { + rowItem.clone.selected = isSelected; + } + rowItem.afterSelectionChange(rowItem); + } + }; + + // @return - boolean indicating if all items are selected or not + // @val - boolean indicating whether to select all/de-select all + self.toggleSelectAll = function (checkAll, bypass, selectFiltered) { + var rows = selectFiltered ? grid.filteredRows : grid.rowCache; + if (bypass || grid.config.beforeSelectionChange(rows, checkAll)) { + var selectedlength = self.selectedItems.length; + if (selectedlength > 0) { + self.selectedItems.length = 0; + } + for (var i = 0; i < rows.length; i++) { + rows[i].selected = checkAll; + if (rows[i].clone) { + rows[i].clone.selected = checkAll; + } + if (checkAll) { + self.selectedItems.push(rows[i].entity); + } + } + if (!bypass) { + grid.config.afterSelectionChange(rows, checkAll); + } + } + }; +}; +var ngStyleProvider = function($scope, grid) { + $scope.headerCellStyle = function(col) { + return { "height": col.headerRowHeight + "px" }; + }; + $scope.rowStyle = function (row) { + var ret = { "top": row.offsetTop + "px", "height": $scope.rowHeight + "px" }; + if (row.isAggRow) { + ret.left = row.offsetLeft; + } + return ret; + }; + $scope.canvasStyle = function() { + return { "height": grid.maxCanvasHt + "px" }; + }; + $scope.headerScrollerStyle = function() { + return { "height": grid.config.headerRowHeight + "px" }; + }; + $scope.topPanelStyle = function() { + return { "width": grid.rootDim.outerWidth + "px", "height": $scope.topPanelHeight() + "px" }; + }; + $scope.headerStyle = function() { + return { "width": grid.rootDim.outerWidth + "px", "height": grid.config.headerRowHeight + "px" }; + }; + $scope.groupPanelStyle = function () { + return { "width": grid.rootDim.outerWidth + "px", "height": "32px" }; + }; + $scope.viewportStyle = function() { + return { "width": grid.rootDim.outerWidth + "px", "height": $scope.viewportDimHeight() + "px" }; + }; + $scope.footerStyle = function() { + return { "width": grid.rootDim.outerWidth + "px", "height": $scope.footerRowHeight + "px" }; + }; +}; +ngGridDirectives.directive('ngCellHasFocus', ['$domUtilityService', + function (domUtilityService) { + var focusOnInputElement = function($scope, elm) { + $scope.isFocused = true; + domUtilityService.digest($scope); + + $scope.$broadcast('ngGridEventStartCellEdit'); + + $scope.$on('ngGridEventEndCellEdit', function() { + $scope.isFocused = false; + domUtilityService.digest($scope); + }); + }; + + return function($scope, elm) { + var isFocused = false; + var isCellEditableOnMouseDown = false; + + $scope.editCell = function() { + if(!$scope.enableCellEditOnFocus) { + setTimeout(function() { + focusOnInputElement($scope,elm); + }, 0); + } + }; + elm.bind('mousedown', function(evt) { + if($scope.enableCellEditOnFocus) { + isCellEditableOnMouseDown = true; + } else { + elm.focus(); + } + return true; + }); + elm.bind('click', function(evt) { + if($scope.enableCellEditOnFocus) { + evt.preventDefault(); + isCellEditableOnMouseDown = false; + focusOnInputElement($scope,elm); + } + }); + elm.bind('focus', function(evt) { + isFocused = true; + if($scope.enableCellEditOnFocus && !isCellEditableOnMouseDown) { + focusOnInputElement($scope,elm); + } + return true; + }); + elm.bind('blur', function() { + isFocused = false; + return true; + }); + elm.bind('keydown', function(evt) { + if(!$scope.enableCellEditOnFocus) { + if (isFocused && evt.keyCode !== 37 && evt.keyCode !== 38 && evt.keyCode !== 39 && evt.keyCode !== 40 && evt.keyCode !== 9 && !evt.shiftKey && evt.keyCode !== 13) { + focusOnInputElement($scope,elm); + } + if (isFocused && evt.shiftKey && (evt.keyCode >= 65 && evt.keyCode <= 90)) { + focusOnInputElement($scope, elm); + } + if (evt.keyCode === 27) { + elm.focus(); + } + } + return true; + }); + }; + }]); +ngGridDirectives.directive('ngCellText', + function () { + return function(scope, elm) { + elm.bind('mouseover', function(evt) { + evt.preventDefault(); + elm.css({ + 'cursor': 'text' + }); + }); + elm.bind('mouseleave', function(evt) { + evt.preventDefault(); + elm.css({ + 'cursor': 'default' + }); + }); + }; + }); +ngGridDirectives.directive('ngCell', ['$compile', '$domUtilityService', function ($compile, domUtilityService) { + var ngCell = { + scope: false, + compile: function() { + return { + pre: function($scope, iElement) { + var html; + var cellTemplate = $scope.col.cellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field); + + if ($scope.col.enableCellEdit) { + html = $scope.col.cellEditTemplate; + html = html.replace(DISPLAY_CELL_TEMPLATE, cellTemplate); + html = html.replace(EDITABLE_CELL_TEMPLATE, $scope.col.editableCellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field)); + } else { + html = cellTemplate; + } + + var cellElement = $compile(html)($scope); + + if ($scope.enableCellSelection && cellElement[0].className.indexOf('ngSelectionCell') === -1) { + cellElement[0].setAttribute('tabindex', 0); + cellElement.addClass('ngCellElement'); + } + + iElement.append(cellElement); + }, + post: function($scope, iElement) { + if ($scope.enableCellSelection) { + $scope.domAccessProvider.selectionHandlers($scope, iElement); + } + + $scope.$on('ngGridEventDigestCell', function() { + domUtilityService.digest($scope); + }); + } + }; + } + }; + + return ngCell; +}]); +/* + * Defines the ui-if tag. This removes/adds an element from the dom depending on a condition + * Originally created by @tigbro, for the @jquery-mobile-angular-adapter + * https://github.com/tigbro/jquery-mobile-angular-adapter + */ +ngGridDirectives.directive('ngEditCellIf', [function () { + return { + transclude: 'element', + priority: 1000, + terminal: true, + restrict: 'A', + compile: function (e, a, transclude) { + return function (scope, element, attr) { + + var childElement; + var childScope; + + scope.$watch(attr['ngEditCellIf'], function (newValue) { + if (childElement) { + childElement.remove(); + childElement = undefined; + } + if (childScope) { + childScope.$destroy(); + childScope = undefined; + } + + if (newValue) { + childScope = scope.$new(); + transclude(childScope, function (clone) { + childElement = clone; + element.after(clone); + }); + } + }); + }; + } + }; +}]); +ngGridDirectives.directive('ngGridFooter', ['$compile', '$templateCache', function ($compile, $templateCache) { + var ngGridFooter = { + scope: false, + compile: function () { + return { + pre: function ($scope, iElement) { + if (iElement.children().length === 0) { + iElement.append($compile($templateCache.get($scope.gridId + 'footerTemplate.html'))($scope)); + } + } + }; + } + }; + return ngGridFooter; +}]); +ngGridDirectives.directive('ngGridMenu', ['$compile', '$templateCache', function ($compile, $templateCache) { + var ngGridMenu = { + scope: false, + compile: function () { + return { + pre: function ($scope, iElement) { + if (iElement.children().length === 0) { + iElement.append($compile($templateCache.get($scope.gridId + 'menuTemplate.html'))($scope)); + } + } + }; + } + }; + return ngGridMenu; +}]); +ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) { + var ngGridDirective = { + scope: true, + compile: function() { + return { + pre: function($scope, iElement, iAttrs) { + var $element = $(iElement); + var options = $scope.$eval(iAttrs.ngGrid); + options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() }); + + var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q); + return grid.init().then(function() { + // if columndefs are a string of a property ont he scope watch for changes and rebuild columns. + if (typeof options.columnDefs === "string") { + $scope.$parent.$watch(options.columnDefs, function (a) { + if (!a) { + grid.refreshDomSizes(); + grid.buildColumns(); + return; + } + // we have to set this to false in case we want to autogenerate columns with no initial data. + grid.lateBoundColumns = false; + $scope.columns = []; + grid.config.columnDefs = a; + grid.buildColumns(); + grid.eventProvider.assignEvents(); + domUtilityService.RebuildGrid($scope, grid); + }, true); + } + else { + grid.buildColumns(); + } + + // Watch totalServerItems if it's a string + if (typeof options.totalServerItems === "string") { + $scope.$parent.$watch(options.totalServerItems, function (newTotal, oldTotal) { + // If the newTotal is not defined (like during init, set the value to 0) + if (!angular.isDefined(newTotal)) { + $scope.totalServerItems = 0; + } + // Otherwise set the value to the new total + else { + $scope.totalServerItems = newTotal; + } + }); + } + // If it's NOT a string, then just set totalServerItems to 0 since they should only be setting this if using a string + else { + $scope.totalServerItems = 0; + } + + // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data + if (typeof options.data === "string") { + var dataWatcher = function (a) { + // make a temporary copy of the data + grid.data = $.extend([], a); + if (grid.config.columnDefsFromData) { + grid.config.columnDefs = undefined; + grid.buildColumns(); + } + grid.rowFactory.fixRowCache(); + angular.forEach(grid.data, function (item, j) { + var indx = grid.rowMap[j] || j; + if (grid.rowCache[indx]) { + grid.rowCache[indx].ensureEntity(item); + } + grid.rowMap[indx] = j; + }); + grid.searchProvider.evalFilter(); + grid.configureColumnWidths(); + grid.refreshDomSizes(); + if (grid.config.sortInfo.fields.length > 0) { + grid.sortColumnsInit(); + $scope.$emit('ngGridEventSorted', grid.config.sortInfo); + } + $scope.$emit("ngGridEventData", grid.gridId); + }; + $scope.$parent.$watch(options.data, dataWatcher); + $scope.$parent.$watch(options.data + '.length', function() { + dataWatcher($scope.$eval(options.data)); + }); + } + + grid.footerController = new ngFooter($scope, grid); + //set the right styling on the container + iElement.addClass("ngGrid").addClass(grid.gridId.toString()); + if (!options.enableHighlighting) { + iElement.addClass("unselectable"); + } + if (options.jqueryUITheme) { + iElement.addClass('ui-widget'); + } + iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic + //walk the element's graph and the correct properties on the grid + domUtilityService.AssignGridContainers($scope, iElement, grid); + //now use the manager to assign the event handlers + grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); + + // method for user to select a specific row programatically + options.selectRow = function (rowIndex, state) { + if (grid.rowCache[rowIndex]) { + if (grid.rowCache[rowIndex].clone) { + grid.rowCache[rowIndex].clone.setSelection(state ? true : false); + } + grid.rowCache[rowIndex].setSelection(state ? true : false); + } + }; + // method for user to select the row by data item programatically + options.selectItem = function (itemIndex, state) { + options.selectRow(grid.rowMap[itemIndex], state); + }; + // method for user to set the select all state. + options.selectAll = function (state) { + $scope.toggleSelectAll(state); + }; + // method for user to set the select all state on visible items. + options.selectVisible = function (state) { + $scope.toggleSelectAll(state, true); + }; + // method for user to set the groups programatically + options.groupBy = function (field) { + if (field) { + $scope.groupBy($scope.columns.filter(function(c) { + return c.field === field; + })[0]); + } else { + var arr = $.extend(true, [], $scope.configGroups); + angular.forEach(arr, $scope.groupBy); + } + }; + // method for user to set the sort field programatically + options.sortBy = function (field) { + var col = $scope.columns.filter(function (c) { + return c.field === field; + })[0]; + if (col) { + col.sort(); + } + }; + // the grid Id, entity, scope for convenience + options.gridId = grid.gridId; + options.ngGrid = grid; + options.$gridScope = $scope; + options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService, UtilityService: $utils }; + $scope.$on('ngGridEventDigestGrid', function(){ + domUtilityService.digest($scope.$parent); + }); + + $scope.$on('ngGridEventDigestGridParent', function(){ + domUtilityService.digest($scope.$parent); + }); + // set up the columns + $scope.$evalAsync(function() { + $scope.adjustScrollLeft(0); + }); + //initialize plugins. + angular.forEach(options.plugins, function (p) { + if (typeof p === "function") { + p = new p(); //If p is a function, then we assume it is a class. + } + p.init($scope.$new(), grid, options.$gridServices); + options.plugins[$utils.getInstanceType(p)] = p; + }); + //send initi finalize notification. + if (typeof options.init === "function") { + options.init(grid, $scope); + } + return null; + }); + } + }; + } + }; + return ngGridDirective; +}]); + +ngGridDirectives.directive('ngHeaderCell', ['$compile', function($compile) { + var ngHeaderCell = { + scope: false, + compile: function() { + return { + pre: function($scope, iElement) { + iElement.append($compile($scope.col.headerCellTemplate)($scope)); + } + }; + } + }; + return ngHeaderCell; +}]); +ngGridDirectives.directive('ngInput', [function() { + return { + require: 'ngModel', + link: function (scope, elm, attrs, ngModel) { + // Store the initial cell value so we can reset to it if need be + var oldCellValue; + var dereg = scope.$watch('ngModel', function() { + oldCellValue = ngModel.$modelValue; + dereg(); // only run this watch once, we don't want to overwrite our stored value when the input changes + }); + + elm.bind('keydown', function(evt) { + switch (evt.keyCode) { + case 37: // Left arrow + case 38: // Up arrow + case 39: // Right arrow + case 40: // Down arrow + evt.stopPropagation(); + break; + case 27: // Esc (reset to old value) + if (!scope.$$phase) { + scope.$apply(function() { + ngModel.$setViewValue(oldCellValue); + elm.blur(); + }); + } + break; + case 13: // Enter (Leave Field) + if(scope.enableCellEditOnFocus && scope.totalFilteredItemsLength() - 1 > scope.row.rowIndex && scope.row.rowIndex > 0 || scope.enableCellEdit) { + elm.blur(); + } + break; + } + + return true; + }); + + elm.bind('click', function(evt) { + evt.stopPropagation(); + }); + + elm.bind('mousedown', function(evt) { + evt.stopPropagation(); + }); + + scope.$on('ngGridEventStartCellEdit', function () { + elm.focus(); + elm.select(); + }); + + angular.element(elm).bind('blur', function () { + scope.$emit('ngGridEventEndCellEdit'); + }); + } + }; +}]); +ngGridDirectives.directive('ngRow', ['$compile', '$domUtilityService', '$templateCache', function ($compile, domUtilityService, $templateCache) { + var ngRow = { + scope: false, + compile: function() { + return { + pre: function($scope, iElement) { + $scope.row.elm = iElement; + if ($scope.row.clone) { + $scope.row.clone.elm = iElement; + } + if ($scope.row.isAggRow) { + var html = $templateCache.get($scope.gridId + 'aggregateTemplate.html'); + if ($scope.row.aggLabelFilter) { + html = html.replace(CUSTOM_FILTERS, '| ' + $scope.row.aggLabelFilter); + } else { + html = html.replace(CUSTOM_FILTERS, ""); + } + iElement.append($compile(html)($scope)); + } else { + iElement.append($compile($templateCache.get($scope.gridId + 'rowTemplate.html'))($scope)); + } + $scope.$on('ngGridEventDigestRow', function(){ + domUtilityService.digest($scope); + }); + } + }; + } + }; + return ngRow; +}]); +ngGridDirectives.directive('ngViewport', [function() { + return function($scope, elm) { + var isMouseWheelActive; + var prevScollLeft; + var prevScollTop = 0; + elm.bind('scroll', function(evt) { + var scrollLeft = evt.target.scrollLeft, + scrollTop = evt.target.scrollTop; + if ($scope.$headerContainer) { + $scope.$headerContainer.scrollLeft(scrollLeft); + } + $scope.adjustScrollLeft(scrollLeft); + $scope.adjustScrollTop(scrollTop); + if (!$scope.$root.$$phase) { + $scope.$digest(); + } + prevScollLeft = scrollLeft; + prevScollTop = scrollTop; + isMouseWheelActive = false; + return true; + }); + elm.bind("mousewheel DOMMouseScroll", function() { + isMouseWheelActive = true; + if (elm.focus) { elm.focus(); } + return true; + }); + if (!$scope.enableCellSelection) { + $scope.domAccessProvider.selectionHandlers($scope, elm); + } + }; +}]); +window.ngGrid.i18n['da'] = { + ngAggregateLabel: 'artikler', + ngGroupPanelDescription: 'Grupér rækker udfra en kolonne ved at trække dens overskift hertil.', + ngSearchPlaceHolder: 'Søg...', + ngMenuText: 'Vælg kolonner:', + ngShowingItemsLabel: 'Viste rækker:', + ngTotalItemsLabel: 'Rækker totalt:', + ngSelectedItemsLabel: 'Valgte rækker:', + ngPageSizeLabel: 'Side størrelse:', + ngPagerFirstTitle: 'Første side', + ngPagerNextTitle: 'Næste side', + ngPagerPrevTitle: 'Forrige side', + ngPagerLastTitle: 'Sidste side' +}; +window.ngGrid.i18n['de'] = { + ngAggregateLabel: 'artikel', + ngGroupPanelDescription: 'Ziehen Sie eine Spaltenüberschrift hier und legen Sie es der Gruppe nach dieser Spalte.', + ngSearchPlaceHolder: 'Suche...', + ngMenuText: 'Spalten auswählen:', + ngShowingItemsLabel: 'Zeige Artikel:', + ngTotalItemsLabel: 'Meiste Artikel:', + ngSelectedItemsLabel: 'Ausgewählte Artikel:', + ngPageSizeLabel: 'Größe Seite:', + ngPagerFirstTitle: 'Erste Page', + ngPagerNextTitle: 'Nächste Page', + ngPagerPrevTitle: 'Vorherige Page', + ngPagerLastTitle: 'Letzte Page' +}; +window.ngGrid.i18n['en'] = { + ngAggregateLabel: 'items', + ngGroupPanelDescription: 'Drag a column header here and drop it to group by that column.', + ngSearchPlaceHolder: 'Search...', + ngMenuText: 'Choose Columns:', + ngShowingItemsLabel: 'Showing Items:', + ngTotalItemsLabel: 'Total Items:', + ngSelectedItemsLabel: 'Selected Items:', + ngPageSizeLabel: 'Page Size:', + ngPagerFirstTitle: 'First Page', + ngPagerNextTitle: 'Next Page', + ngPagerPrevTitle: 'Previous Page', + ngPagerLastTitle: 'Last Page' +}; +window.ngGrid.i18n['es'] = { + ngAggregateLabel: 'Artículos', + ngGroupPanelDescription: 'Arrastre un encabezado de columna aquí y soltarlo para agrupar por esa columna.', + ngSearchPlaceHolder: 'Buscar...', + ngMenuText: 'Elegir columnas:', + ngShowingItemsLabel: 'Artículos Mostrando:', + ngTotalItemsLabel: 'Artículos Totales:', + ngSelectedItemsLabel: 'Artículos Seleccionados:', + ngPageSizeLabel: 'Tamaño de Página:', + ngPagerFirstTitle: 'Primera Página', + ngPagerNextTitle: 'Página Siguiente', + ngPagerPrevTitle: 'Página Anterior', + ngPagerLastTitle: 'Última Página' +}; +window.ngGrid.i18n['fr'] = { + ngAggregateLabel: 'articles', + ngGroupPanelDescription: 'Faites glisser un en-tête de colonne ici et déposez-le vers un groupe par cette colonne.', + ngSearchPlaceHolder: 'Recherche...', + ngMenuText: 'Choisir des colonnes:', + ngShowingItemsLabel: 'Articles Affichage des:', + ngTotalItemsLabel: 'Nombre total d\'articles:', + ngSelectedItemsLabel: 'Éléments Articles:', + ngPageSizeLabel: 'Taille de page:', + ngPagerFirstTitle: 'Première page', + ngPagerNextTitle: 'Page Suivante', + ngPagerPrevTitle: 'Page précédente', + ngPagerLastTitle: 'Dernière page' +}; +window.ngGrid.i18n['pt-br'] = { + ngAggregateLabel: 'items', + ngGroupPanelDescription: 'Arraste e solte uma coluna aqui para agrupar por essa coluna', + ngSearchPlaceHolder: 'Procurar...', + ngMenuText: 'Selecione as colunas:', + ngShowingItemsLabel: 'Mostrando os Items:', + ngTotalItemsLabel: 'Total de Items:', + ngSelectedItemsLabel: 'Items Selecionados:', + ngPageSizeLabel: 'Tamanho da Página:', + ngPagerFirstTitle: 'Primeira Página', + ngPagerNextTitle: 'Próxima Página', + ngPagerPrevTitle: 'Página Anterior', + ngPagerLastTitle: 'Última Página' +}; +window.ngGrid.i18n['zh-cn'] = { + ngAggregateLabel: '条目', + ngGroupPanelDescription: '拖曳表头到此处以进行分组', + ngSearchPlaceHolder: '搜索...', + ngMenuText: '数据分组与选择列:', + ngShowingItemsLabel: '当前显示条目:', + ngTotalItemsLabel: '条目总数:', + ngSelectedItemsLabel: '选中条目:', + ngPageSizeLabel: '每页显示数:', + ngPagerFirstTitle: '回到首页', + ngPagerNextTitle: '下一页', + ngPagerPrevTitle: '上一页', + ngPagerLastTitle: '前往尾页' +}; + +window.ngGrid.i18n['zh-tw'] = { + ngAggregateLabel: '筆', + ngGroupPanelDescription: '拖拉表頭到此處以進行分組', + ngSearchPlaceHolder: '搜尋...', + ngMenuText: '選擇欄位:', + ngShowingItemsLabel: '目前顯示筆數:', + ngTotalItemsLabel: '總筆數:', + ngSelectedItemsLabel: '選取筆數:', + ngPageSizeLabel: '每頁顯示:', + ngPagerFirstTitle: '第一頁', + ngPagerNextTitle: '下一頁', + ngPagerPrevTitle: '上一頁', + ngPagerLastTitle: '最後頁' +}; + +angular.module("ngGrid").run(["$templateCache", function($templateCache) { + + $templateCache.put("aggregateTemplate.html", + "<div ng-click=\"row.toggleExpand()\" ng-style=\"rowStyle(row)\" class=\"ngAggregate\">" + + " <span class=\"ngAggregateText\">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>" + + " <div class=\"{{row.aggClass()}}\"></div>" + + "</div>" + + "" + ); + + $templateCache.put("cellEditTemplate.html", + "<div ng-cell-has-focus ng-dblclick=\"editCell()\">" + + " <div ng-edit-cell-if=\"!isFocused\"> " + + " DISPLAY_CELL_TEMPLATE" + + " </div>" + + " <div ng-edit-cell-if=\"isFocused\">" + + " EDITABLE_CELL_TEMPLATE" + + " </div>" + + "</div>" + ); + + $templateCache.put("cellTemplate.html", + "<div class=\"ngCellText\" ng-class=\"col.colIndex()\"><span ng-cell-text>{{COL_FIELD CUSTOM_FILTERS}}</span></div>" + ); + + $templateCache.put("checkboxCellTemplate.html", + "<div class=\"ngSelectionCell\"><input tabindex=\"-1\" class=\"ngSelectionCheckbox\" type=\"checkbox\" ng-checked=\"row.selected\" /></div>" + ); + + $templateCache.put("checkboxHeaderTemplate.html", + "<input class=\"ngSelectionHeader\" type=\"checkbox\" ng-show=\"multiSelect\" ng-model=\"allSelected\" ng-change=\"toggleSelectAll(allSelected, true)\"/>" + ); + + $templateCache.put("editableCellTemplate.html", + "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-model=\"COL_FIELD\" />" + ); + + $templateCache.put("footerTemplate.html", + "<div ng-show=\"showFooter\" class=\"ngFooterPanel\" ng-class=\"{'ui-widget-content': jqueryUITheme, 'ui-corner-bottom': jqueryUITheme}\" ng-style=\"footerStyle()\">" + + " <div class=\"ngTotalSelectContainer\" >" + + " <div class=\"ngFooterTotalItems\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\" >" + + " <span class=\"ngLabel\">{{i18n.ngTotalItemsLabel}} {{maxRows()}}</span><span ng-show=\"filterText.length > 0\" class=\"ngLabel\">({{i18n.ngShowingItemsLabel}} {{totalFilteredItemsLength()}})</span>" + + " </div>" + + " <div class=\"ngFooterSelectedItems\" ng-show=\"multiSelect\">" + + " <span class=\"ngLabel\">{{i18n.ngSelectedItemsLabel}} {{selectedItems.length}}</span>" + + " </div>" + + " </div>" + + " <div class=\"ngPagerContainer\" style=\"float: right; margin-top: 10px;\" ng-show=\"enablePaging\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\">" + + " <div style=\"float:left; margin-right: 10px;\" class=\"ngRowCountPicker\">" + + " <span style=\"float: left; margin-top: 3px;\" class=\"ngLabel\">{{i18n.ngPageSizeLabel}}</span>" + + " <select style=\"float: left;height: 27px; width: 100px\" ng-model=\"pagingOptions.pageSize\" >" + + " <option ng-repeat=\"size in pagingOptions.pageSizes\">{{size}}</option>" + + " </select>" + + " </div>" + + " <div style=\"float:left; margin-right: 10px; line-height:25px;\" class=\"ngPagerControl\" style=\"float: left; min-width: 135px;\">" + + " <button class=\"ngPagerButton\" ng-click=\"pageToFirst()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerFirstTitle}}\"><div class=\"ngPagerFirstTriangle\"><div class=\"ngPagerFirstBar\"></div></div></button>" + + " <button class=\"ngPagerButton\" ng-click=\"pageBackward()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerPrevTitle}}\"><div class=\"ngPagerFirstTriangle ngPagerPrevTriangle\"></div></button>" + + " <input class=\"ngPagerCurrent\" min=\"1\" max=\"{{maxPages()}}\" type=\"number\" style=\"width:50px; height: 24px; margin-top: 1px; padding: 0 4px;\" ng-model=\"pagingOptions.currentPage\"/>" + + " <button class=\"ngPagerButton\" ng-click=\"pageForward()\" ng-disabled=\"cantPageForward()\" title=\"{{i18n.ngPagerNextTitle}}\"><div class=\"ngPagerLastTriangle ngPagerNextTriangle\"></div></button>" + + " <button class=\"ngPagerButton\" ng-click=\"pageToLast()\" ng-disabled=\"cantPageToLast()\" title=\"{{i18n.ngPagerLastTitle}}\"><div class=\"ngPagerLastTriangle\"><div class=\"ngPagerLastBar\"></div></div></button>" + + " </div>" + + " </div>" + + "</div>" + ); + + $templateCache.put("gridTemplate.html", + "<div class=\"ngTopPanel\" ng-class=\"{'ui-widget-header':jqueryUITheme, 'ui-corner-top': jqueryUITheme}\" ng-style=\"topPanelStyle()\">" + + " <div class=\"ngGroupPanel\" ng-show=\"showGroupPanel()\" ng-style=\"groupPanelStyle()\">" + + " <div class=\"ngGroupPanelDescription\" ng-show=\"configGroups.length == 0\">{{i18n.ngGroupPanelDescription}}</div>" + + " <ul ng-show=\"configGroups.length > 0\" class=\"ngGroupList\">" + + " <li class=\"ngGroupItem\" ng-repeat=\"group in configGroups\">" + + " <span class=\"ngGroupElement\">" + + " <span class=\"ngGroupName\">{{group.displayName}}" + + " <span ng-click=\"removeGroup($index)\" class=\"ngRemoveGroup\">x</span>" + + " </span>" + + " <span ng-hide=\"$last\" class=\"ngGroupArrow\"></span>" + + " </span>" + + " </li>" + + " </ul>" + + " </div>" + + " <div class=\"ngHeaderContainer\" ng-style=\"headerStyle()\">" + + " <div class=\"ngHeaderScroller\" ng-style=\"headerScrollerStyle()\" ng-include=\"gridId + 'headerRowTemplate.html'\"></div>" + + " </div>" + + " <div ng-grid-menu></div>" + + "</div>" + + "<div class=\"ngViewport\" unselectable=\"on\" ng-viewport ng-class=\"{'ui-widget-content': jqueryUITheme}\" ng-style=\"viewportStyle()\">" + + " <div class=\"ngCanvas\" ng-style=\"canvasStyle()\">" + + " <div ng-style=\"rowStyle(row)\" ng-repeat=\"row in renderedRows\" ng-click=\"row.toggleSelected($event)\" ng-class=\"row.alternatingRowClass()\" ng-row></div>" + + " </div>" + + "</div>" + + "<div ng-grid-footer></div>" + + "" + ); + + $templateCache.put("headerCellTemplate.html", + "<div class=\"ngHeaderSortColumn {{col.headerClass}}\" ng-style=\"{'cursor': col.cursor}\" ng-class=\"{ 'ngSorted': !noSortVisible }\">" + + " <div ng-click=\"col.sort($event)\" ng-class=\"'colt' + col.index\" class=\"ngHeaderText\">{{col.displayName}}</div>" + + " <div class=\"ngSortButtonDown\" ng-show=\"col.showSortButtonDown()\"></div>" + + " <div class=\"ngSortButtonUp\" ng-show=\"col.showSortButtonUp()\"></div>" + + " <div class=\"ngSortPriority\">{{col.sortPriority}}</div>" + + " <div ng-class=\"{ ngPinnedIcon: col.pinned, ngUnPinnedIcon: !col.pinned }\" ng-click=\"togglePin(col)\" ng-show=\"col.pinnable\"></div>" + + "</div>" + + "<div ng-show=\"col.resizable\" class=\"ngHeaderGrip\" ng-click=\"col.gripClick($event)\" ng-mousedown=\"col.gripOnMouseDown($event)\"></div>" + ); + + $templateCache.put("headerRowTemplate.html", + "<div ng-style=\"{ height: col.headerRowHeight }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngHeaderCell\">" + + " <div class=\"ngVerticalBar\" ng-style=\"{height: col.headerRowHeight}\" ng-class=\"{ ngVerticalBarVisible: !$last }\"> </div>" + + " <div ng-header-cell></div>" + + "</div>" + ); + + $templateCache.put("menuTemplate.html", + "<div ng-show=\"showColumnMenu || showFilter\" class=\"ngHeaderButton\" ng-click=\"toggleShowMenu()\">" + + " <div class=\"ngHeaderButtonArrow\"></div>" + + "</div>" + + "<div ng-show=\"showMenu\" class=\"ngColMenu\">" + + " <div ng-show=\"showFilter\">" + + " <input placeholder=\"{{i18n.ngSearchPlaceHolder}}\" type=\"text\" ng-model=\"filterText\"/>" + + " </div>" + + " <div ng-show=\"showColumnMenu\">" + + " <span class=\"ngMenuText\">{{i18n.ngMenuText}}</span>" + + " <ul class=\"ngColList\">" + + " <li class=\"ngColListItem\" ng-repeat=\"col in columns | ngColumns\">" + + " <label><input ng-disabled=\"col.pinned\" type=\"checkbox\" class=\"ngColListCheckbox\" ng-model=\"col.visible\"/>{{col.displayName}}</label>" + + " <a title=\"Group By\" ng-class=\"col.groupedByClass()\" ng-show=\"col.groupable && col.visible\" ng-click=\"groupBy(col)\"></a>" + + " <span class=\"ngGroupingNumber\" ng-show=\"col.groupIndex > 0\">{{col.groupIndex}}</span> " + + " </li>" + + " </ul>" + + " </div>" + + "</div>" + ); + + $templateCache.put("rowTemplate.html", + "<div ng-style=\"{ 'cursor': row.cursor }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngCell {{col.cellClass}}\">" + + " <div class=\"ngVerticalBar\" ng-style=\"{height: rowHeight}\" ng-class=\"{ ngVerticalBarVisible: !$last }\"> </div>" + + " <div ng-cell></div>" + + "</div>" + ); + +}]); + +}(window, jQuery)); \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.min.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.min.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/ng-grid-2.0.7.min.js 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,2 @@ +(function(e,t){"use strict";var n=6,o=4,i="asc",r="desc",l="_ng_field_",a="_ng_depth_",s="_ng_hidden_",c="_ng_column_",g=/CUSTOM_FILTERS/g,d=/COL_FIELD/g,u=/DISPLAY_CELL_TEMPLATE/g,f=/EDITABLE_CELL_TEMPLATE/g,h=/<.+>/;e.ngGrid={},e.ngGrid.i18n={},angular.module("ngGrid.services",[]);var p=angular.module("ngGrid.directives",[]),m=angular.module("ngGrid.filters",[]);angular.module("ngGrid",["ngGrid.services","ngGrid.directives","ngGrid.filters"]);var v=function(e,t,o,i){if(void 0===e.selectionProvider.selectedItems)return!0;var r,l=o.which||o.keyCode,a=!1,s=!1,c=void 0===e.selectionProvider.lastClickedRow?1:e.selectionProvider.lastClickedRow.rowIndex,g=e.columns.filter(function(e){return e.visible}),d=e.columns.filter(function(e){return e.pinned});if(e.col&&(r=g.indexOf(e.col)),37!==l&&38!==l&&39!==l&&40!==l&&9!==l&&13!==l)return!0;if(e.enableCellSelection){9===l&&o.preventDefault();var u=e.showSelectionCheckbox?1===e.col.index:0===e.col.index,f=1===e.$index||0===e.$index,h=e.$index===e.renderedColumns.length-1||e.$index===e.renderedColumns.length-2,p=g.indexOf(e.col)===g.length-1,m=d.indexOf(e.col)===d.length-1;if(37===l||9===l&&o.shiftKey){var v=0;u||(r-=1),f?u&&9===l&&o.shiftKey?(v=i.$canvas.width(),r=g.length-1,s=!0):v=i.$viewport.scrollLeft()-e.col.width:d.length>0&&(v=i.$viewport.scrollLeft()-g[r].width),i.$viewport.scrollLeft(v)}else(39===l||9===l&&!o.shiftKey)&&(h?p&&9===l&&!o.shiftKey?(i.$viewport.scrollLeft(0),r=e.showSelectionCheckbox?1:0,a=!0):i.$viewport.scrollLeft(i.$viewport.scrollLeft()+e.col.width):m&&i.$viewport.scrollLeft(0),p||(r+=1))}var w;w=e.configGroups.length>0?i.rowFactory.parsedData.filter(function(e){return!e.isAggRow}):i.filteredRows;var C=0;if(0!==c&&(38===l||13===l&&o.shiftKey||9===l&&o.shiftKey&&s)?C=-1:c!==w.length-1&&(40===l||13===l&&!o.shiftKey||9===l&&a)&&(C=1),C){var b=w[c+C];b.beforeSelectionChange(b,o)&&(b.continueSelection(o),e.$emit("ngGridEventDigestGridParent"),e.selectionProvider.lastClickedRow.renderedRowIndex>=e.renderedRows.length-n-2?i.$viewport.scrollTop(i.$viewport.scrollTop()+e.rowHeight):n+2>=e.selectionProvider.lastClickedRow.renderedRowIndex&&i.$viewport.scrollTop(i.$viewport.scrollTop()-e.rowHeight))}return e.enableCellSelection&&setTimeout(function(){e.domAccessProvider.focusCellElement(e,e.renderedColumns.indexOf(g[r]))},3),!1};String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Array.prototype.indexOf||(Array.prototype.indexOf=function(e){var t=this.length>>>0,n=Number(arguments[1])||0;for(n=0>n?Math.ceil(n):Math.floor(n),0>n&&(n+=t);t>n;n++)if(n in this&&this[n]===e)return n;return-1}),Array.prototype.filter||(Array.prototype.filter=function(e){var t=Object(this),n=t.length>>>0;if("function"!=typeof e)throw new TypeError;for(var o=[],i=arguments[1],r=0;n>r;r++)if(r in t){var l=t[r];e.call(i,l,r,t)&&o.push(l)}return o}),m.filter("checkmark",function(){return function(e){return e?"✔":"✘"}}),m.filter("ngColumns",function(){return function(e){return e.filter(function(e){return!e.isAggCol})}}),angular.module("ngGrid.services").factory("$domUtilityService",["$utilityService",function(e){var n={},o={},i=function(){var e=t("<div></div>");e.appendTo("body"),e.height(100).width(100).css("position","absolute").css("overflow","scroll"),e.append('<div style="height: 400px; width: 400px;"></div>'),n.ScrollH=e.height()-e[0].clientHeight,n.ScrollW=e.width()-e[0].clientWidth,e.empty(),e.attr("style",""),e.append('<span style="font-family: Verdana, Helvetica, Sans-Serif; font-size: 14px;"><strong>M</strong></span>'),n.LetterW=e.children().first().width(),e.remove()};return n.eventStorage={},n.AssignGridContainers=function(e,o,i){i.$root=t(o),i.$topPanel=i.$root.find(".ngTopPanel"),i.$groupPanel=i.$root.find(".ngGroupPanel"),i.$headerContainer=i.$topPanel.find(".ngHeaderContainer"),e.$headerContainer=i.$headerContainer,i.$headerScroller=i.$topPanel.find(".ngHeaderScroller"),i.$headers=i.$headerScroller.children(),i.$viewport=i.$root.find(".ngViewport"),i.$canvas=i.$viewport.find(".ngCanvas"),i.$footerPanel=i.$root.find(".ngFooterPanel"),e.$watch(function(){return i.$viewport.scrollLeft()},function(e){return i.$headerContainer.scrollLeft(e)}),n.UpdateGridLayout(e,i)},n.getRealWidth=function(e){var n=0,o={visibility:"hidden",display:"block"},i=e.parents().andSelf().not(":visible");return t.swap(i[0],o,function(){n=e.outerWidth()}),n},n.UpdateGridLayout=function(e,t){var o=t.$viewport.scrollTop();t.elementDims.rootMaxW=t.$root.width(),t.$root.is(":hidden")&&(t.elementDims.rootMaxW=n.getRealWidth(t.$root)),t.elementDims.rootMaxH=t.$root.height(),t.refreshDomSizes(),e.adjustScrollTop(o,!0)},n.numberOfGrids=0,n.BuildStyles=function(o,i,r){var l,a=i.config.rowHeight,s=i.$styleSheet,c=i.gridId,g=o.columns,d=0;s||(s=t("#"+c),s[0]||(s=t("<style id='"+c+"' type='text/css' rel='stylesheet' />").appendTo(i.$root))),s.empty();var u=o.totalRowWidth();l="."+c+" .ngCanvas { width: "+u+"px; }"+"."+c+" .ngRow { width: "+u+"px; }"+"."+c+" .ngCanvas { width: "+u+"px; }"+"."+c+" .ngHeaderScroller { width: "+(u+n.ScrollH)+"px}";for(var f=0;g.length>f;f++){var h=g[f];h.visible!==!1&&(l+="."+c+" .col"+f+" { width: "+h.width+"px; left: "+d+"px; height: "+a+"px }"+"."+c+" .colt"+f+" { width: "+h.width+"px; }",d+=h.width)}e.isIe?s[0].styleSheet.cssText=l:s[0].appendChild(document.createTextNode(l)),i.$styleSheet=s,o.adjustScrollLeft(i.$viewport.scrollLeft()),r&&n.digest(o)},n.setColLeft=function(t,n,i){if(i.$styleSheet){var r=o[t.index];r||(r=o[t.index]=RegExp(".col"+t.index+" { width: [0-9]+px; left: [0-9]+px"));var l=i.$styleSheet.html(),a=l.replace(r,".col"+t.index+" { width: "+t.width+"px; left: "+n+"px");e.isIe?setTimeout(function(){i.$styleSheet.html(a)}):i.$styleSheet.html(a)}},n.setColLeft.immediate=1,n.RebuildGrid=function(e,t){n.UpdateGridLayout(e,t),(null==t.config.maintainColumnRatios||t.config.maintainColumnRatios)&&t.configureColumnWidths(),e.adjustScrollLeft(t.$viewport.scrollLeft()),n.BuildStyles(e,t,!0)},n.digest=function(e){e.$root.$$phase||e.$digest()},n.ScrollH=17,n.ScrollW=17,n.LetterW=10,i(),n}]),angular.module("ngGrid.services").factory("$sortService",["$parse",function(e){var t={};return t.colSortFnCache={},t.guessSortFn=function(e){var n=typeof e;switch(n){case"number":return t.sortNumber;case"boolean":return t.sortBool;case"string":return e.match(/^[-+]?[£$¤]?[\d,.]+%?$/)?t.sortNumberStr:t.sortAlpha;default:return"[object Date]"===Object.prototype.toString.call(e)?t.sortDate:t.basicSort}},t.basicSort=function(e,t){return e===t?0:t>e?-1:1},t.sortNumber=function(e,t){return e-t},t.sortNumberStr=function(e,t){var n,o,i=!1,r=!1;return n=parseFloat(e.replace(/[^0-9.-]/g,"")),isNaN(n)&&(i=!0),o=parseFloat(t.replace(/[^0-9.-]/g,"")),isNaN(o)&&(r=!0),i&&r?0:i?1:r?-1:n-o},t.sortAlpha=function(e,t){var n=e.toLowerCase(),o=t.toLowerCase();return n===o?0:o>n?-1:1},t.sortDate=function(e,t){var n=e.getTime(),o=t.getTime();return n===o?0:o>n?-1:1},t.sortBool=function(e,t){return e&&t?0:e||t?e?1:-1:0},t.sortData=function(n,o){if(o&&n){var r,l,a=n.fields.length,s=n.fields,c=o.slice(0);o.sort(function(o,g){for(var d,u=0,f=0;0===u&&a>f;){r=n.columns[f],l=n.directions[f],d=t.getSortFn(r,c);var h=e(s[f])(o),p=e(s[f])(g);!h&&0!==h||!p&&0!==p?p||h?h?p||(u=-1):u=1:u=0:u=d(h,p),f++}return l===i?u:0-u})}},t.Sort=function(e,n){t.isSorting||(t.isSorting=!0,t.sortData(e,n),t.isSorting=!1)},t.getSortFn=function(n,o){var i,r;if(t.colSortFnCache[n.field])i=t.colSortFnCache[n.field];else if(void 0!==n.sortingAlgorithm)i=n.sortingAlgorithm,t.colSortFnCache[n.field]=n.sortingAlgorithm;else{if(r=o[0],!r)return i;i=t.guessSortFn(e(n.field)(r)),i?t.colSortFnCache[n.field]=i:i=t.sortAlpha}return i},t}]),angular.module("ngGrid.services").factory("$utilityService",["$parse",function(n){var o=/function (.{1,})\(/,i={visualLength:function(e){var n=document.getElementById("testDataLength");return n||(n=document.createElement("SPAN"),n.id="testDataLength",n.style.visibility="hidden",document.body.appendChild(n)),t(n).css("font",t(e).css("font")),t(n).css("font-size",t(e).css("font-size")),t(n).css("font-family",t(e).css("font-family")),n.innerHTML=t(e).text(),n.offsetWidth},forIn:function(e,t){for(var n in e)e.hasOwnProperty(n)&&t(e[n],n)},evalProperty:function(e,t){return n(t)(e)},endsWith:function(e,t){return e&&t&&"string"==typeof e?-1!==e.indexOf(t,e.length-t.length):!1},isNullOrUndefined:function(e){return void 0===e||null===e?!0:!1},getElementsByClassName:function(e){for(var t=[],n=RegExp("\\b"+e+"\\b"),o=document.getElementsByTagName("*"),i=0;o.length>i;i++){var r=o[i].className;n.test(r)&&t.push(o[i])}return t},newId:function(){var e=(new Date).getTime();return function(){return e+=1}}(),seti18n:function(t,n){var o=e.ngGrid.i18n[n];for(var i in o)t.i18n[i]=o[i]},getInstanceType:function(e){var t=o.exec(""+e.constructor);if(t&&t.length>1){var n=t[1].replace(/^\s+|\s+$/g,"");return n}return""},ieVersion:function(){var e=3,t=document.createElement("div"),n=t.getElementsByTagName("i");do t.innerHTML="<!--[if gt IE "+ ++e+"]><i></i><![endif]-->";while(n[0]);return e>4?e:void 0}()};return t.extend(i,{isIe:function(){return void 0!==i.ieVersion}()}),i}]);var w=function(e,t,n,o){this.rowIndex=0,this.offsetTop=this.rowIndex*n,this.entity=e,this.label=e.gLabel,this.field=e.gField,this.depth=e.gDepth,this.parent=e.parent,this.children=e.children,this.aggChildren=e.aggChildren,this.aggIndex=e.aggIndex,this.collapsed=o,this.groupInitState=o,this.rowFactory=t,this.rowHeight=n,this.isAggRow=!0,this.offsetLeft=25*e.gDepth,this.aggLabelFilter=e.aggLabelFilter};w.prototype.toggleExpand=function(){this.collapsed=this.collapsed?!1:!0,this.orig&&(this.orig.collapsed=this.collapsed),this.notifyChildren()},w.prototype.setExpand=function(e){this.collapsed=e,this.notifyChildren()},w.prototype.notifyChildren=function(){for(var e=Math.max(this.rowFactory.aggCache.length,this.children.length),t=0;e>t;t++)if(this.aggChildren[t]&&(this.aggChildren[t].entity[s]=this.collapsed,this.collapsed&&this.aggChildren[t].setExpand(this.collapsed)),this.children[t]&&(this.children[t][s]=this.collapsed),t>this.aggIndex&&this.rowFactory.aggCache[t]){var n=this.rowFactory.aggCache[t],o=30*this.children.length;n.offsetTop=this.collapsed?n.offsetTop-o:n.offsetTop+o}this.rowFactory.renderedChange()},w.prototype.aggClass=function(){return this.collapsed?"ngAggArrowCollapsed":"ngAggArrowExpanded"},w.prototype.totalChildren=function(){if(this.aggChildren.length>0){var e=0,t=function(n){n.aggChildren.length>0?angular.forEach(n.aggChildren,function(e){t(e)}):e+=n.children.length};return t(this),e}return this.children.length},w.prototype.copy=function(){var e=new w(this.entity,this.rowFactory,this.rowHeight,this.groupInitState);return e.orig=this,e};var C=function(e,n,o,l,a,s){var c=this,d=e.colDef,u=500,f=0,p=null;c.colDef=e.colDef,c.width=d.width,c.groupIndex=0,c.isGroupedBy=!1,c.minWidth=d.minWidth?d.minWidth:50,c.maxWidth=d.maxWidth?d.maxWidth:9e3,c.enableCellEdit=void 0!==d.enableCellEdit?d.enableCellEdit:e.enableCellEdit||e.enableCellEditOnFocus,c.headerRowHeight=e.headerRowHeight,c.displayName=void 0===d.displayName?d.field:d.displayName,c.index=e.index,c.isAggCol=e.isAggCol,c.cellClass=d.cellClass,c.sortPriority=void 0,c.cellFilter=d.cellFilter?d.cellFilter:"",c.field=d.field,c.aggLabelFilter=d.cellFilter||d.aggLabelFilter,c.visible=s.isNullOrUndefined(d.visible)||d.visible,c.sortable=!1,c.resizable=!1,c.pinnable=!1,c.pinned=e.enablePinning&&d.pinned,c.originalIndex=null==e.originalIndex?c.index:e.originalIndex,c.groupable=s.isNullOrUndefined(d.groupable)||d.groupable,e.enableSort&&(c.sortable=s.isNullOrUndefined(d.sortable)||d.sortable),e.enableResize&&(c.resizable=s.isNullOrUndefined(d.resizable)||d.resizable),e.enablePinning&&(c.pinnable=s.isNullOrUndefined(d.pinnable)||d.pinnable),c.sortDirection=void 0,c.sortingAlgorithm=d.sortFn,c.headerClass=d.headerClass,c.cursor=c.sortable?"pointer":"default",c.headerCellTemplate=d.headerCellTemplate||a.get("headerCellTemplate.html"),c.cellTemplate=d.cellTemplate||a.get("cellTemplate.html").replace(g,c.cellFilter?"|"+c.cellFilter:""),c.enableCellEdit&&(c.cellEditTemplate=a.get("cellEditTemplate.html"),c.editableCellTemplate=d.editableCellTemplate||a.get("editableCellTemplate.html")),d.cellTemplate&&!h.test(d.cellTemplate)&&(c.cellTemplate=t.ajax({type:"GET",url:d.cellTemplate,async:!1}).responseText),c.enableCellEdit&&d.editableCellTemplate&&!h.test(d.editableCellTemplate)&&(c.editableCellTemplate=t.ajax({type:"GET",url:d.editableCellTemplate,async:!1}).responseText),d.headerCellTemplate&&!h.test(d.headerCellTemplate)&&(c.headerCellTemplate=t.ajax({type:"GET",url:d.headerCellTemplate,async:!1}).responseText),c.colIndex=function(){var e=c.pinned?"pinned ":"";return e+="col"+c.index+" colt"+c.index,c.cellClass&&(e+=" "+c.cellClass),e},c.groupedByClass=function(){return c.isGroupedBy?"ngGroupedByIcon":"ngGroupIcon"},c.toggleVisible=function(){c.visible=!c.visible},c.showSortButtonUp=function(){return c.sortable?c.sortDirection===r:c.sortable},c.showSortButtonDown=function(){return c.sortable?c.sortDirection===i:c.sortable},c.noSortVisible=function(){return!c.sortDirection},c.sort=function(t){if(!c.sortable)return!0;var n=c.sortDirection===i?r:i;return c.sortDirection=n,e.sortCallback(c,t),!1},c.gripClick=function(){f++,1===f?p=setTimeout(function(){f=0},u):(clearTimeout(p),e.resizeOnDataCallback(c),f=0)},c.gripOnMouseDown=function(e){return n.isColumnResizing=!0,e.ctrlKey&&!c.pinned?(c.toggleVisible(),l.BuildStyles(n,o),!0):(e.target.parentElement.style.cursor="col-resize",c.startMousePosition=e.clientX,c.origWidth=c.width,t(document).mousemove(c.onMouseMove),t(document).mouseup(c.gripOnMouseUp),!1)},c.onMouseMove=function(e){var t=e.clientX-c.startMousePosition,i=t+c.origWidth;return c.width=c.minWidth>i?c.minWidth:i>c.maxWidth?c.maxWidth:i,n.hasUserChangedGridColumnWidths=!0,l.BuildStyles(n,o),!1},c.gripOnMouseUp=function(e){return t(document).off("mousemove",c.onMouseMove),t(document).off("mouseup",c.gripOnMouseUp),e.target.parentElement.style.cursor="default",l.digest(n),n.isColumnResizing=!1,!1},c.copy=function(){var t=new C(e,n,o,l,a);return t.isClone=!0,t.orig=c,t},c.setVars=function(e){c.orig=e,c.width=e.width,c.groupIndex=e.groupIndex,c.isGroupedBy=e.isGroupedBy,c.displayName=e.displayName,c.index=e.index,c.isAggCol=e.isAggCol,c.cellClass=e.cellClass,c.cellFilter=e.cellFilter,c.field=e.field,c.aggLabelFilter=e.aggLabelFilter,c.visible=e.visible,c.sortable=e.sortable,c.resizable=e.resizable,c.pinnable=e.pinnable,c.pinned=e.pinned,c.originalIndex=e.originalIndex,c.sortDirection=e.sortDirection,c.sortingAlgorithm=e.sortingAlgorithm,c.headerClass=e.headerClass,c.headerCellTemplate=e.headerCellTemplate,c.cellTemplate=e.cellTemplate,c.cellEditTemplate=e.cellEditTemplate}},b=function(e){this.outerHeight=null,this.outerWidth=null,t.extend(this,e)},S=function(e){this.previousColumn=null,this.grid=e};S.prototype.changeUserSelect=function(e,t){e.css({"-webkit-touch-callout":t,"-webkit-user-select":t,"-khtml-user-select":t,"-moz-user-select":"none"===t?"-moz-none":t,"-ms-user-select":t,"user-select":t})},S.prototype.focusCellElement=function(e,t){if(e.selectionProvider.lastClickedRow){var n=void 0!==t?t:this.previousColumn,o=e.selectionProvider.lastClickedRow.clone?e.selectionProvider.lastClickedRow.clone.elm:e.selectionProvider.lastClickedRow.elm;if(void 0!==n&&o){var i=angular.element(o[0].children).filter(function(){return 8!==this.nodeType}),r=Math.max(Math.min(e.renderedColumns.length-1,n),0);this.grid.config.showSelectionCheckbox&&angular.element(i[r]).scope()&&0===angular.element(i[r]).scope().col.index&&(r=1),i[r]&&i[r].children[1].children[0].focus(),this.previousColumn=n}}},S.prototype.selectionHandlers=function(e,t){var n=!1,o=this;t.bind("keydown",function(i){if(16===i.keyCode)return o.changeUserSelect(t,"none",i),!0;if(!n){n=!0;var r=v(e,t,i,o.grid);return n=!1,r}return!0}),t.bind("keyup",function(e){return 16===e.keyCode&&o.changeUserSelect(t,"text",e),!0})};var x=function(n,o,i,r){var l=this;l.colToMove=void 0,l.groupToMove=void 0,l.assignEvents=function(){n.config.jqueryUIDraggable&&!n.config.enablePinning?n.$groupPanel.droppable({addClasses:!1,drop:function(e){l.onGroupDrop(e)}}):(n.$groupPanel.on("mousedown",l.onGroupMouseDown).on("dragover",l.dragOver).on("drop",l.onGroupDrop),n.$headerScroller.on("mousedown",l.onHeaderMouseDown).on("dragover",l.dragOver),n.config.enableColumnReordering&&!n.config.enablePinning&&n.$headerScroller.on("drop",l.onHeaderDrop)),o.$watch("renderedColumns",function(){r(l.setDraggables)})},l.dragStart=function(e){e.dataTransfer.setData("text","")},l.dragOver=function(e){e.preventDefault()},l.setDraggables=function(){if(n.config.jqueryUIDraggable)n.$root.find(".ngHeaderSortColumn").draggable({helper:"clone",appendTo:"body",stack:"div",addClasses:!1,start:function(e){l.onHeaderMouseDown(e)}}).droppable({drop:function(e){l.onHeaderDrop(e)}});else{var e=n.$root.find(".ngHeaderSortColumn");angular.forEach(e,function(e){e.className&&-1!==e.className.indexOf("ngHeaderSortColumn")&&(e.setAttribute("draggable","true"),e.addEventListener&&e.addEventListener("dragstart",l.dragStart))}),-1!==navigator.userAgent.indexOf("MSIE")&&n.$root.find(".ngHeaderSortColumn").bind("selectstart",function(){return this.dragDrop(),!1})}},l.onGroupMouseDown=function(e){var o=t(e.target);if("ngRemoveGroup"!==o[0].className){var i=angular.element(o).scope();i&&(n.config.jqueryUIDraggable||(o.attr("draggable","true"),this.addEventListener&&this.addEventListener("dragstart",l.dragStart),-1!==navigator.userAgent.indexOf("MSIE")&&o.bind("selectstart",function(){return this.dragDrop(),!1})),l.groupToMove={header:o,groupName:i.group,index:i.$index})}else l.groupToMove=void 0},l.onGroupDrop=function(e){e.stopPropagation();var i,r;l.groupToMove?(i=t(e.target).closest(".ngGroupElement"),"ngGroupPanel"===i.context.className?(o.configGroups.splice(l.groupToMove.index,1),o.configGroups.push(l.groupToMove.groupName)):(r=angular.element(i).scope(),r&&l.groupToMove.index!==r.$index&&(o.configGroups.splice(l.groupToMove.index,1),o.configGroups.splice(r.$index,0,l.groupToMove.groupName))),l.groupToMove=void 0,n.fixGroupIndexes()):l.colToMove&&(-1===o.configGroups.indexOf(l.colToMove.col)&&(i=t(e.target).closest(".ngGroupElement"),"ngGroupPanel"===i.context.className||"ngGroupPanelDescription ng-binding"===i.context.className?o.groupBy(l.colToMove.col):(r=angular.element(i).scope(),r&&o.removeGroup(r.$index))),l.colToMove=void 0),o.$$phase||o.$apply()},l.onHeaderMouseDown=function(e){var n=t(e.target).closest(".ngHeaderSortColumn"),o=angular.element(n).scope();o&&(l.colToMove={header:n,col:o.col})},l.onHeaderDrop=function(e){if(l.colToMove&&!l.colToMove.col.pinned){var r=t(e.target).closest(".ngHeaderSortColumn"),a=angular.element(r).scope();if(a){if(l.colToMove.col===a.col)return;o.columns.splice(l.colToMove.col.index,1),o.columns.splice(a.col.index,0,l.colToMove.col),n.fixColumnIndexes(),l.colToMove=void 0,i.digest(o)}}},l.assignGridEventHandlers=function(){-1===n.config.tabIndex?(n.$viewport.attr("tabIndex",i.numberOfGrids),i.numberOfGrids++):n.$viewport.attr("tabIndex",n.config.tabIndex);var r;t(e).resize(function(){clearTimeout(r),r=setTimeout(function(){i.RebuildGrid(o,n)},100)});var l;t(n.$root.parent()).on("resize",function(){clearTimeout(l),l=setTimeout(function(){i.RebuildGrid(o,n)},100)})},l.assignGridEventHandlers(),l.assignEvents()},y=function(e,t){e.maxRows=function(){var n=Math.max(e.totalServerItems,t.data.length);return n},e.multiSelect=t.config.enableRowSelection&&t.config.multiSelect,e.selectedItemCount=t.selectedItemCount,e.maxPages=function(){return Math.ceil(e.maxRows()/e.pagingOptions.pageSize)},e.pageForward=function(){var t=e.pagingOptions.currentPage;e.totalServerItems>0?e.pagingOptions.currentPage=Math.min(t+1,e.maxPages()):e.pagingOptions.currentPage++},e.pageBackward=function(){var t=e.pagingOptions.currentPage;e.pagingOptions.currentPage=Math.max(t-1,1)},e.pageToFirst=function(){e.pagingOptions.currentPage=1},e.pageToLast=function(){var t=e.maxPages();e.pagingOptions.currentPage=t},e.cantPageForward=function(){var n=e.pagingOptions.currentPage,o=e.maxPages();return e.totalServerItems>0?n>=o:1>t.data.length},e.cantPageToLast=function(){return e.totalServerItems>0?e.cantPageForward():!0},e.cantPageBackward=function(){var t=e.pagingOptions.currentPage;return 1>=t}},T=function(i,r,l,a,c,g,d,u,f,p,m){var v={aggregateTemplate:void 0,afterSelectionChange:function(){},beforeSelectionChange:function(){return!0},checkboxCellTemplate:void 0,checkboxHeaderTemplate:void 0,columnDefs:void 0,data:[],dataUpdated:function(){},enableCellEdit:!1,enableCellEditOnFocus:!1,enableCellSelection:!1,enableColumnResize:!1,enableColumnReordering:!1,enableColumnHeavyVirt:!1,enablePaging:!1,enablePinning:!1,enableRowSelection:!0,enableSorting:!0,enableHighlighting:!1,excludeProperties:[],filterOptions:{filterText:"",useExternalFilter:!1},footerRowHeight:55,footerTemplate:void 0,groups:[],groupsCollapsedByDefault:!0,headerRowHeight:30,headerRowTemplate:void 0,jqueryUIDraggable:!1,jqueryUITheme:!1,keepLastSelected:!0,maintainColumnRatios:void 0,menuTemplate:void 0,multiSelect:!0,pagingOptions:{pageSizes:[250,500,1e3],pageSize:250,currentPage:1},pinSelectionCheckbox:!1,plugins:[],primaryKey:void 0,rowHeight:30,rowTemplate:void 0,selectedItems:[],selectWithCheckboxOnly:!1,showColumnMenu:!1,showFilter:!1,showFooter:!1,showGroupPanel:!1,showSelectionCheckbox:!1,sortInfo:{fields:[],columns:[],directions:[]},tabIndex:-1,totalServerItems:0,useExternalSorting:!1,i18n:"en",virtualizationThreshold:50},w=this;w.maxCanvasHt=0,w.config=t.extend(v,e.ngGrid.config,r),w.config.showSelectionCheckbox=w.config.showSelectionCheckbox&&w.config.enableColumnHeavyVirt===!1,w.config.enablePinning=w.config.enablePinning&&w.config.enableColumnHeavyVirt===!1,w.config.selectWithCheckboxOnly=w.config.selectWithCheckboxOnly&&w.config.showSelectionCheckbox!==!1,w.config.pinSelectionCheckbox=w.config.enablePinning,"string"==typeof r.columnDefs&&(w.config.columnDefs=i.$eval(r.columnDefs)),w.rowCache=[],w.rowMap=[],w.gridId="ng"+d.newId(),w.$root=null,w.$groupPanel=null,w.$topPanel=null,w.$headerContainer=null,w.$headerScroller=null,w.$headers=null,w.$viewport=null,w.$canvas=null,w.rootDim=w.config.gridDim,w.data=[],w.lateBindColumns=!1,w.filteredRows=[],w.initTemplates=function(){var e=["rowTemplate","aggregateTemplate","headerRowTemplate","checkboxCellTemplate","checkboxHeaderTemplate","menuTemplate","footerTemplate"],t=[];return angular.forEach(e,function(e){t.push(w.getTemplate(e))}),m.all(t)},w.getTemplate=function(e){var t=w.config[e],n=w.gridId+e+".html",o=m.defer();if(t&&!h.test(t))p.get(t,{cache:g}).success(function(e){g.put(n,e),o.resolve()}).error(function(){o.reject("Could not load template: "+t)});else if(t)g.put(n,t),o.resolve();else{var i=e+".html";g.put(n,g.get(i)),o.resolve()}return o.promise},"object"==typeof w.config.data&&(w.data=w.config.data),w.calcMaxCanvasHeight=function(){var e;return e=w.config.groups.length>0?w.rowFactory.parsedData.filter(function(e){return!e[s]}).length*w.config.rowHeight:w.filteredRows.length*w.config.rowHeight},w.elementDims={scrollW:0,scrollH:0,rowIndexCellW:25,rowSelectedCellW:25,rootMaxW:0,rootMaxH:0},w.setRenderedRows=function(e){i.renderedRows.length=e.length;for(var t=0;e.length>t;t++)!i.renderedRows[t]||e[t].isAggRow||i.renderedRows[t].isAggRow?(i.renderedRows[t]=e[t].copy(),i.renderedRows[t].collapsed=e[t].collapsed,e[t].isAggRow||i.renderedRows[t].setVars(e[t])):i.renderedRows[t].setVars(e[t]),i.renderedRows[t].rowIndex=e[t].rowIndex,i.renderedRows[t].offsetTop=e[t].offsetTop,i.renderedRows[t].selected=e[t].selected,e[t].renderedRowIndex=t;w.refreshDomSizes(),i.$emit("ngGridEventRows",e)},w.minRowsToRender=function(){var e=i.viewportDimHeight()||1;return Math.floor(e/w.config.rowHeight)},w.refreshDomSizes=function(){var e=new b;e.outerWidth=w.elementDims.rootMaxW,e.outerHeight=w.elementDims.rootMaxH,w.rootDim=e,w.maxCanvasHt=w.calcMaxCanvasHeight()},w.buildColumnDefsFromData=function(){w.config.columnDefs=[];var e=w.data[0];return e?(d.forIn(e,function(e,t){-1===w.config.excludeProperties.indexOf(t)&&w.config.columnDefs.push({field:t})}),void 0):(w.lateBoundColumns=!0,void 0)},w.buildColumns=function(){var e=w.config.columnDefs,t=[];if(e||(w.buildColumnDefsFromData(),e=w.config.columnDefs),w.config.showSelectionCheckbox&&t.push(new C({colDef:{field:"✔",width:w.elementDims.rowSelectedCellW,sortable:!1,resizable:!1,groupable:!1,headerCellTemplate:g.get(i.gridId+"checkboxHeaderTemplate.html"),cellTemplate:g.get(i.gridId+"checkboxCellTemplate.html"),pinned:w.config.pinSelectionCheckbox},index:0,headerRowHeight:w.config.headerRowHeight,sortCallback:w.sortData,resizeOnDataCallback:w.resizeOnData,enableResize:w.config.enableColumnResize,enableSort:w.config.enableSorting,enablePinning:w.config.enablePinning},i,w,a,g,d)),e.length>0){var n=w.config.showSelectionCheckbox?1:0,o=i.configGroups.length;i.configGroups.length=0,angular.forEach(e,function(e,r){r+=n;var l=new C({colDef:e,index:r+o,originalIndex:r,headerRowHeight:w.config.headerRowHeight,sortCallback:w.sortData,resizeOnDataCallback:w.resizeOnData,enableResize:w.config.enableColumnResize,enableSort:w.config.enableSorting,enablePinning:w.config.enablePinning,enableCellEdit:w.config.enableCellEdit||w.config.enableCellEditOnFocus},i,w,a,g,d),s=w.config.groups.indexOf(e.field);-1!==s&&(l.isGroupedBy=!0,i.configGroups.splice(s,0,l),l.groupIndex=i.configGroups.length),t.push(l)}),i.columns=t,i.configGroups.length>0&&w.rowFactory.getGrouping(i.configGroups)}},w.configureColumnWidths=function(){var e=[],t=[],n=0,o=0,r={};if(angular.forEach(i.columns,function(e,t){if(!d.isNullOrUndefined(e.originalIndex)){var n=e.originalIndex;w.config.showSelectionCheckbox&&(0===e.originalIndex&&e.visible&&(o+=25),n--),r[n]=t}}),angular.forEach(w.config.columnDefs,function(l,a){var s=i.columns[r[a]];l.index=a;var c,g=!1;if(d.isNullOrUndefined(l.width)?l.width="*":(g=isNaN(l.width)?d.endsWith(l.width,"%"):!1,c=g?l.width:parseInt(l.width,10)),isNaN(c)&&!i.hasUserChangedGridColumnWidths){if(c=l.width,"auto"===c){s.width=s.minWidth,o+=s.width;var u=s;return i.$on("ngGridEventData",function(){w.resizeOnData(u)}),void 0}if(-1!==c.indexOf("*"))return s.visible!==!1&&(n+=c.length),e.push(l),void 0;if(g)return t.push(l),void 0;throw'unable to parse column width, use percentage ("10%","20%", etc...) or "*" to use remaining width of grid'}s.visible!==!1&&(o+=s.width=parseInt(s.width,10))}),t.length>0){w.config.maintainColumnRatios=w.config.maintainColumnRatios!==!1;var l=0,s=0;angular.forEach(t,function(e){var t=i.columns[r[e.index]],n=e.width,o=parseInt(n.slice(0,-1),10)/100;l+=o,t.visible||(s+=o)});var c=l-s;angular.forEach(t,function(e){var t=i.columns[r[e.index]],n=e.width,a=parseInt(n.slice(0,-1),10)/100;a/=s>0?c:l;var g=w.rootDim.outerWidth*l;t.width=Math.floor(g*a),o+=t.width})}if(e.length>0){w.config.maintainColumnRatios=w.config.maintainColumnRatios!==!1;var g=w.rootDim.outerWidth-o;w.maxCanvasHt>i.viewportDimHeight()&&(g-=a.ScrollW);var u=Math.floor(g/n);angular.forEach(e,function(t,n){var l=i.columns[r[t.index]];l.width=u*t.width.length,l.visible!==!1&&(o+=l.width);var s=n===e.length-1;if(s&&w.rootDim.outerWidth>o){var c=w.rootDim.outerWidth-o;w.maxCanvasHt>i.viewportDimHeight()&&(c-=a.ScrollW),l.width+=c}})}},w.init=function(){return w.initTemplates().then(function(){i.selectionProvider=new D(w,i,f),i.domAccessProvider=new S(w),w.rowFactory=new R(w,i,a,g,d),w.searchProvider=new $(i,w,c),w.styleProvider=new G(i,w),i.$watch("configGroups",function(e){var t=[];angular.forEach(e,function(e){t.push(e.field||e)}),w.config.groups=t,w.rowFactory.filteredRowsChanged(),i.$emit("ngGridEventGroups",e)},!0),i.$watch("columns",function(e){i.isColumnResizing||a.RebuildGrid(i,w),i.$emit("ngGridEventColumns",e)},!0),i.$watch(function(){return r.i18n},function(e){d.seti18n(i,e)}),w.maxCanvasHt=w.calcMaxCanvasHeight(),w.config.sortInfo.fields&&w.config.sortInfo.fields.length>0&&i.$watch(function(){return w.config.sortInfo},function(){l.isSorting||(w.sortColumnsInit(),i.$emit("ngGridEventSorted",w.config.sortInfo))},!0)})},w.resizeOnData=function(e){var n=e.minWidth,o=d.getElementsByClassName("col"+e.index);angular.forEach(o,function(e,o){var i;if(0===o){var r=t(e).find(".ngHeaderText");i=d.visualLength(r)+10}else{var l=t(e).find(".ngCellText");i=d.visualLength(l)+10}i>n&&(n=i)}),e.width=e.longest=Math.min(e.maxWidth,n+7),a.BuildStyles(i,w,!0)},w.lastSortedColumns=[],w.sortData=function(e,n){if(n&&n.shiftKey&&w.config.sortInfo){var o=w.config.sortInfo.columns.indexOf(e);-1===o?(1===w.config.sortInfo.columns.length&&(w.config.sortInfo.columns[0].sortPriority=1),w.config.sortInfo.columns.push(e),e.sortPriority=w.config.sortInfo.columns.length,w.config.sortInfo.fields.push(e.field),w.config.sortInfo.directions.push(e.sortDirection),w.lastSortedColumns.push(e)):w.config.sortInfo.directions[o]=e.sortDirection}else{var r=t.isArray(e);w.config.sortInfo.columns.length=0,w.config.sortInfo.fields.length=0,w.config.sortInfo.directions.length=0;var l=function(e){w.config.sortInfo.columns.push(e),w.config.sortInfo.fields.push(e.field),w.config.sortInfo.directions.push(e.sortDirection),w.lastSortedColumns.push(e)};r?(w.clearSortingData(),angular.forEach(e,function(e,t){e.sortPriority=t+1,l(e)})):(w.clearSortingData(e),e.sortPriority=void 0,l(e))}w.sortActual(),w.searchProvider.evalFilter(),i.$emit("ngGridEventSorted",w.config.sortInfo)},w.sortColumnsInit=function(){w.config.sortInfo.columns?w.config.sortInfo.columns.length=0:w.config.sortInfo.columns=[],angular.forEach(i.columns,function(e){var t=w.config.sortInfo.fields.indexOf(e.field);-1!==t&&(e.sortDirection=w.config.sortInfo.directions[t]||"asc",w.config.sortInfo.columns[t]=e)}),angular.forEach(w.config.sortInfo.columns,function(e){w.sortData(e)})},w.sortActual=function(){if(!w.config.useExternalSorting){var e=w.data.slice(0);angular.forEach(e,function(e,t){var n=w.rowMap[t];if(void 0!==n){var o=w.rowCache[n];void 0!==o&&(e.preSortSelected=o.selected,e.preSortIndex=t)}}),l.Sort(w.config.sortInfo,e),angular.forEach(e,function(e,t){w.rowCache[t].entity=e,w.rowCache[t].selected=e.preSortSelected,w.rowMap[e.preSortIndex]=t,delete e.preSortSelected,delete e.preSortIndex})}},w.clearSortingData=function(e){e?(angular.forEach(w.lastSortedColumns,function(t){e.index!==t.index&&(t.sortDirection="",t.sortPriority=null)}),w.lastSortedColumns[0]=e,w.lastSortedColumns.length=1):(angular.forEach(w.lastSortedColumns,function(e){e.sortDirection="",e.sortPriority=null}),w.lastSortedColumns=[])},w.fixColumnIndexes=function(){for(var e=0;i.columns.length>e;e++)i.columns[e].index=e},w.fixGroupIndexes=function(){angular.forEach(i.configGroups,function(e,t){e.groupIndex=t+1})},i.elementsNeedMeasuring=!0,i.columns=[],i.renderedRows=[],i.renderedColumns=[],i.headerRow=null,i.rowHeight=w.config.rowHeight,i.jqueryUITheme=w.config.jqueryUITheme,i.showSelectionCheckbox=w.config.showSelectionCheckbox,i.enableCellSelection=w.config.enableCellSelection,i.enableCellEditOnFocus=w.config.enableCellEditOnFocus,i.footer=null,i.selectedItems=w.config.selectedItems,i.multiSelect=w.config.multiSelect,i.showFooter=w.config.showFooter,i.footerRowHeight=i.showFooter?w.config.footerRowHeight:0,i.showColumnMenu=w.config.showColumnMenu,i.showMenu=!1,i.configGroups=[],i.gridId=w.gridId,i.enablePaging=w.config.enablePaging,i.pagingOptions=w.config.pagingOptions,i.i18n={},d.seti18n(i,w.config.i18n),i.adjustScrollLeft=function(e){for(var t=0,n=0,o=i.columns.length,r=[],l=!w.config.enableColumnHeavyVirt,s=0,c=function(e){l?r.push(e):i.renderedColumns[s]?i.renderedColumns[s].setVars(e):i.renderedColumns[s]=e.copy(),s++},g=0;o>g;g++){var d=i.columns[g];if(d.visible!==!1){var u=d.width+t;if(d.pinned){c(d);var f=g>0?e+n:e;a.setColLeft(d,f,w),n+=d.width}else u>=e&&e+w.rootDim.outerWidth>=t&&c(d);t+=d.width}}l&&(i.renderedColumns=r)},w.prevScrollTop=0,w.prevScrollIndex=0,i.adjustScrollTop=function(e,t){if(w.prevScrollTop!==e||t){e>0&&w.$viewport[0].scrollHeight-e<=w.$viewport.outerHeight()&&i.$emit("ngGridEventScroll"); +var r,l=Math.floor(e/w.config.rowHeight);if(w.filteredRows.length>w.config.virtualizationThreshold){if(e>w.prevScrollTop&&w.prevScrollIndex+o>l)return;if(w.prevScrollTop>e&&l>w.prevScrollIndex-o)return;r=new P(Math.max(0,l-n),l+w.minRowsToRender()+n)}else{var a=i.configGroups.length>0?w.rowFactory.parsedData.length:w.data.length;r=new P(0,Math.max(a,w.minRowsToRender()+n))}w.prevScrollTop=e,w.rowFactory.UpdateViewableRange(r),w.prevScrollIndex=l}},i.toggleShowMenu=function(){i.showMenu=!i.showMenu},i.toggleSelectAll=function(e,t){i.selectionProvider.toggleSelectAll(e,!1,t)},i.totalFilteredItemsLength=function(){return w.filteredRows.length},i.showGroupPanel=function(){return w.config.showGroupPanel},i.topPanelHeight=function(){return w.config.showGroupPanel===!0?w.config.headerRowHeight+32:w.config.headerRowHeight},i.viewportDimHeight=function(){return Math.max(0,w.rootDim.outerHeight-i.topPanelHeight()-i.footerRowHeight-2)},i.groupBy=function(e){if(!(1>w.data.length)&&e.groupable&&e.field){e.sortDirection||e.sort({shiftKey:i.configGroups.length>0?!0:!1});var t=i.configGroups.indexOf(e);-1===t?(e.isGroupedBy=!0,i.configGroups.push(e),e.groupIndex=i.configGroups.length):i.removeGroup(t),w.$viewport.scrollTop(0),a.digest(i)}},i.removeGroup=function(e){var t=i.columns.filter(function(t){return t.groupIndex===e+1})[0];t.isGroupedBy=!1,t.groupIndex=0,i.columns[e].isAggCol&&(i.columns.splice(e,1),i.configGroups.splice(e,1),w.fixGroupIndexes()),0===i.configGroups.length&&(w.fixColumnIndexes(),a.digest(i)),i.adjustScrollLeft(0)},i.togglePin=function(e){for(var t=e.index,n=0,o=0;i.columns.length>o&&i.columns[o].pinned;o++)n++;e.pinned&&(n=Math.max(e.originalIndex,n-1)),e.pinned=!e.pinned,i.columns.splice(t,1),i.columns.splice(n,0,e),w.fixColumnIndexes(),a.BuildStyles(i,w,!0),w.$viewport.scrollLeft(w.$viewport.scrollLeft()-e.width)},i.totalRowWidth=function(){for(var e=0,t=i.columns,n=0;t.length>n;n++)t[n].visible!==!1&&(e+=t[n].width);return e},i.headerScrollerDim=function(){var e=i.viewportDimHeight(),t=w.maxCanvasHt,n=t>e,o=new b;return o.autoFitHeight=!0,o.outerWidth=i.totalRowWidth(),n?o.outerWidth+=w.elementDims.scrollW:w.elementDims.scrollH>=t-e&&(o.outerWidth+=w.elementDims.scrollW),o}},P=function(e,t){this.topRow=e,this.bottomRow=t},I=function(e,t,n,o,i){this.entity=e,this.config=t,this.selectionProvider=n,this.rowIndex=o,this.utils=i,this.selected=n.getSelection(e),this.cursor=this.config.enableRowSelection?"pointer":"default",this.beforeSelectionChange=t.beforeSelectionChangeCallback,this.afterSelectionChange=t.afterSelectionChangeCallback,this.offsetTop=this.rowIndex*t.rowHeight,this.rowDisplayIndex=0};I.prototype.setSelection=function(e){this.selectionProvider.setSelection(this,e),this.selectionProvider.lastClickedRow=this},I.prototype.continueSelection=function(e){this.selectionProvider.ChangeSelection(this,e)},I.prototype.ensureEntity=function(e){this.entity!==e&&(this.entity=e,this.selected=this.selectionProvider.getSelection(this.entity))},I.prototype.toggleSelected=function(e){if(!this.config.enableRowSelection&&!this.config.enableCellSelection)return!0;var t=e.target||e;return"checkbox"===t.type&&"ngSelectionCell ng-scope"!==t.parentElement.className?!0:this.config.selectWithCheckboxOnly&&"checkbox"!==t.type?(this.selectionProvider.lastClickedRow=this,!0):(this.beforeSelectionChange(this,e)&&this.continueSelection(e),!1)},I.prototype.alternatingRowClass=function(){var e=0===this.rowIndex%2,t={ngRow:!0,selected:this.selected,even:e,odd:!e,"ui-state-default":this.config.jqueryUITheme&&e,"ui-state-active":this.config.jqueryUITheme&&!e};return t},I.prototype.getProperty=function(e){return this.utils.evalProperty(this.entity,e)},I.prototype.copy=function(){return this.clone=new I(this.entity,this.config,this.selectionProvider,this.rowIndex,this.utils),this.clone.isClone=!0,this.clone.elm=this.elm,this.clone.orig=this,this.clone},I.prototype.setVars=function(e){e.clone=this,this.entity=e.entity,this.selected=e.selected,this.orig=e};var R=function(e,t,o,i,r){var g=this;g.aggCache={},g.parentCache=[],g.dataChanged=!0,g.parsedData=[],g.rowConfig={},g.selectionProvider=t.selectionProvider,g.rowHeight=30,g.numberOfAggregates=0,g.groupedData=void 0,g.rowHeight=e.config.rowHeight,g.rowConfig={enableRowSelection:e.config.enableRowSelection,rowClasses:e.config.rowClasses,selectedItems:t.selectedItems,selectWithCheckboxOnly:e.config.selectWithCheckboxOnly,beforeSelectionChangeCallback:e.config.beforeSelectionChange,afterSelectionChangeCallback:e.config.afterSelectionChange,jqueryUITheme:e.config.jqueryUITheme,enableCellSelection:e.config.enableCellSelection,rowHeight:e.config.rowHeight},g.renderedRange=new P(0,e.minRowsToRender()+n),g.buildEntityRow=function(e,t){return new I(e,g.rowConfig,g.selectionProvider,t,r)},g.buildAggregateRow=function(t,n){var o=g.aggCache[t.aggIndex];return o||(o=new w(t,g,g.rowConfig.rowHeight,e.config.groupsCollapsedByDefault),g.aggCache[t.aggIndex]=o),o.rowIndex=n,o.offsetTop=n*g.rowConfig.rowHeight,o},g.UpdateViewableRange=function(e){g.renderedRange=e,g.renderedChange()},g.filteredRowsChanged=function(){e.lateBoundColumns&&e.filteredRows.length>0&&(e.config.columnDefs=void 0,e.buildColumns(),e.lateBoundColumns=!1,t.$evalAsync(function(){t.adjustScrollLeft(0)})),g.dataChanged=!0,e.config.groups.length>0&&g.getGrouping(e.config.groups),g.UpdateViewableRange(g.renderedRange)},g.renderedChange=function(){if(!g.groupedData||1>e.config.groups.length)return g.renderedChangeNoGroups(),e.refreshDomSizes(),void 0;g.wasGrouped=!0,g.parentCache=[];var t=0,n=g.parsedData.filter(function(e){return e.isAggRow?e.parent&&e.parent.collapsed?!1:!0:(e[s]||(e.rowIndex=t++),!e[s])});g.totalRows=n.length;for(var o=[],i=g.renderedRange.topRow;g.renderedRange.bottomRow>i;i++)n[i]&&(n[i].offsetTop=i*e.config.rowHeight,o.push(n[i]));e.setRenderedRows(o)},g.renderedChangeNoGroups=function(){for(var t=[],n=g.renderedRange.topRow;g.renderedRange.bottomRow>n;n++)e.filteredRows[n]&&(e.filteredRows[n].rowIndex=n,e.filteredRows[n].offsetTop=n*e.config.rowHeight,t.push(e.filteredRows[n]));e.setRenderedRows(t)},g.fixRowCache=function(){var t=e.data.length,n=t-e.rowCache.length;if(0>n)e.rowCache.length=e.rowMap.length=t;else for(var o=e.rowCache.length;t>o;o++)e.rowCache[o]=e.rowFactory.buildEntityRow(e.data[o],o)},g.parseGroupData=function(e){if(e.values)for(var t=0;e.values.length>t;t++)g.parentCache[g.parentCache.length-1].children.push(e.values[t]),g.parsedData.push(e.values[t]);else for(var n in e)if(n!==l&&n!==a&&n!==c&&e.hasOwnProperty(n)){var o=g.buildAggregateRow({gField:e[l],gLabel:n,gDepth:e[a],isAggRow:!0,_ng_hidden_:!1,children:[],aggChildren:[],aggIndex:g.numberOfAggregates,aggLabelFilter:e[c].aggLabelFilter},0);g.numberOfAggregates++,o.parent=g.parentCache[o.depth-1],o.parent&&(o.parent.collapsed=!1,o.parent.aggChildren.push(o)),g.parsedData.push(o),g.parentCache[o.depth]=o,g.parseGroupData(e[n])}},g.getGrouping=function(n){function d(e,t){return e.filter(function(e){return e.field===t})}g.aggCache=[],g.numberOfAggregates=0,g.groupedData={};for(var u=e.filteredRows,f=n.length,h=t.columns,p=0;u.length>p;p++){var m=u[p].entity;if(!m)return;u[p][s]=e.config.groupsCollapsedByDefault;for(var v=g.groupedData,w=0;n.length>w;w++){var b=n[w],S=d(h,b)[0],x=r.evalProperty(m,b);x=x?""+x:"null",v[x]||(v[x]={}),v[l]||(v[l]=b),v[a]||(v[a]=w),v[c]||(v[c]=S),v=v[x]}v.values||(v.values=[]),v.values.push(u[p])}for(var y=0;n.length>y;y++)!h[y].isAggCol&&f>=y&&h.splice(0,0,new C({colDef:{field:"",width:25,sortable:!1,resizable:!1,headerCellTemplate:'<div class="ngAggHeader"></div>',pinned:e.config.pinSelectionCheckbox},enablePinning:e.config.enablePinning,isAggCol:!0,headerRowHeight:e.config.headerRowHeight},t,e,o,i,r));e.fixColumnIndexes(),t.adjustScrollLeft(0),g.parsedData.length=0,g.parseGroupData(g.groupedData),g.fixRowCache()},e.config.groups.length>0&&e.filteredRows.length>0&&g.getGrouping(e.config.groups)},$=function(e,n,o){var i=this,r=[];i.extFilter=n.config.filterOptions.useExternalFilter,e.showFilter=n.config.showFilter,e.filterText="",i.fieldMap={},i.evalFilter=function(){var e=function(e){for(var t=0,n=r.length;n>t;t++){var a,s=r[t];if(!s.column){for(var c in e)if(e.hasOwnProperty(c)){var g=i.fieldMap[c.toLowerCase()];if(!g)continue;var d=null,u=null;g&&g.cellFilter&&(u=g.cellFilter.split(":"),d=o(u[0]));var f=e[c];if(null!==f&&void 0!==f){if("function"==typeof d){var h=""+d("object"==typeof f?l(f,g.field):f,u[1]);a=s.regex.test(h)}else a=s.regex.test("object"==typeof f?""+l(f,g.field):""+f);if(a)return!0}}return!1}var p=i.fieldMap[s.columnDisplay];if(!p)return!1;var m=p.cellFilter.split(":"),v=p.cellFilter?o(m[0]):null,w=e[s.column]||e[p.field.split(".")[0]];if(null===w||void 0===w)return!1;if("function"==typeof v){var C=""+v("object"==typeof w?l(w,p.field):w,m[1]);a=s.regex.test(C)}else a=s.regex.test("object"==typeof w?""+l(w,p.field):""+w);if(!a)return!1}return!0};n.filteredRows=0===r.length?n.rowCache:n.rowCache.filter(function(t){return e(t.entity)});for(var t=0;n.filteredRows.length>t;t++)n.filteredRows[t].rowIndex=t;n.rowFactory.filteredRowsChanged()};var l=function(e,t){if("object"!=typeof e||"string"!=typeof t)return e;var n=t.split("."),o=e;if(n.length>1){for(var i=1,r=n.length;r>i;i++)if(o=o[n[i]],!o)return e;return o}return e},a=function(e,t){try{return RegExp(e,t)}catch(n){return RegExp(e.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g,"\\$1"))}},s=function(e){r=[];var n;if(n=t.trim(e))for(var o=n.split(";"),i=0;o.length>i;i++){var l=o[i].split(":");if(l.length>1){var s=t.trim(l[0]),c=t.trim(l[1]);s&&c&&r.push({column:s,columnDisplay:s.replace(/\s+/g,"").toLowerCase(),regex:a(c,"i")})}else{var g=t.trim(l[0]);g&&r.push({column:"",regex:a(g,"i")})}}};i.extFilter||e.$watch("columns",function(e){for(var t=0;e.length>t;t++){var n=e[t];n.field&&(i.fieldMap[n.field.split(".")[0].toLowerCase()]=n),n.displayName&&(i.fieldMap[n.displayName.toLowerCase().replace(/\s+/g,"")]=n)}}),e.$watch(function(){return n.config.filterOptions.filterText},function(t){e.filterText=t}),e.$watch("filterText",function(t){i.extFilter||(e.$emit("ngGridEventFilter",t),s(t),i.evalFilter())})},D=function(e,t,n){var o=this;o.multi=e.config.multiSelect,o.selectedItems=e.config.selectedItems,o.selectedIndex=e.config.selectedIndex,o.lastClickedRow=void 0,o.ignoreSelectedItemChanges=!1,o.pKeyParser=n(e.config.primaryKey),o.ChangeSelection=function(n,i){var r=i.which||i.keyCode,l=40===r||38===r;if(i&&i.shiftKey&&!i.keyCode&&o.multi&&e.config.enableRowSelection){if(o.lastClickedRow){var a;a=t.configGroups.length>0?e.rowFactory.parsedData.filter(function(e){return!e.isAggRow}):e.filteredRows;var s=n.rowIndex,c=o.lastClickedRowIndex;if(s===c)return!1;c>s?(s^=c,c=s^c,s^=c,s--):c++;for(var g=[];s>=c;c++)g.push(a[c]);if(g[g.length-1].beforeSelectionChange(g,i)){for(var d=0;g.length>d;d++){var u=g[d],f=u.selected;u.selected=!f,u.clone&&(u.clone.selected=u.selected);var h=o.selectedItems.indexOf(u.entity);-1===h?o.selectedItems.push(u.entity):o.selectedItems.splice(h,1)}g[g.length-1].afterSelectionChange(g,i)}return o.lastClickedRow=n,o.lastClickedRowIndex=n.rowIndex,!0}}else o.multi?(!i.keyCode||l&&!e.config.selectWithCheckboxOnly)&&o.setSelection(n,!n.selected):o.lastClickedRow===n?o.setSelection(o.lastClickedRow,e.config.keepLastSelected?!0:!n.selected):(o.lastClickedRow&&o.setSelection(o.lastClickedRow,!1),o.setSelection(n,!n.selected));return o.lastClickedRow=n,o.lastClickedRowIndex=n.rowIndex,!0},o.getSelection=function(t){var n=!1;if(e.config.primaryKey){var i=o.pKeyParser(t);angular.forEach(o.selectedItems,function(e){i===o.pKeyParser(e)&&(n=!0)})}else n=-1!==o.selectedItems.indexOf(t);return n},o.setSelection=function(t,n){if(e.config.enableRowSelection){if(n)-1===o.selectedItems.indexOf(t.entity)&&(!o.multi&&o.selectedItems.length>0&&o.toggleSelectAll(!1,!0),o.selectedItems.push(t.entity));else{var i=o.selectedItems.indexOf(t.entity);-1!==i&&o.selectedItems.splice(i,1)}t.selected=n,t.orig&&(t.orig.selected=n),t.clone&&(t.clone.selected=n),t.afterSelectionChange(t)}},o.toggleSelectAll=function(t,n,i){var r=i?e.filteredRows:e.rowCache;if(n||e.config.beforeSelectionChange(r,t)){var l=o.selectedItems.length;l>0&&(o.selectedItems.length=0);for(var a=0;r.length>a;a++)r[a].selected=t,r[a].clone&&(r[a].clone.selected=t),t&&o.selectedItems.push(r[a].entity);n||e.config.afterSelectionChange(r,t)}}},G=function(e,t){e.headerCellStyle=function(e){return{height:e.headerRowHeight+"px"}},e.rowStyle=function(t){var n={top:t.offsetTop+"px",height:e.rowHeight+"px"};return t.isAggRow&&(n.left=t.offsetLeft),n},e.canvasStyle=function(){return{height:t.maxCanvasHt+"px"}},e.headerScrollerStyle=function(){return{height:t.config.headerRowHeight+"px"}},e.topPanelStyle=function(){return{width:t.rootDim.outerWidth+"px",height:e.topPanelHeight()+"px"}},e.headerStyle=function(){return{width:t.rootDim.outerWidth+"px",height:t.config.headerRowHeight+"px"}},e.groupPanelStyle=function(){return{width:t.rootDim.outerWidth+"px",height:"32px"}},e.viewportStyle=function(){return{width:t.rootDim.outerWidth+"px",height:e.viewportDimHeight()+"px"}},e.footerStyle=function(){return{width:t.rootDim.outerWidth+"px",height:e.footerRowHeight+"px"}}};p.directive("ngCellHasFocus",["$domUtilityService",function(e){var t=function(t){t.isFocused=!0,e.digest(t),t.$broadcast("ngGridEventStartCellEdit"),t.$on("ngGridEventEndCellEdit",function(){t.isFocused=!1,e.digest(t)})};return function(e,n){var o=!1,i=!1;e.editCell=function(){e.enableCellEditOnFocus||setTimeout(function(){t(e,n)},0)},n.bind("mousedown",function(){return e.enableCellEditOnFocus?i=!0:n.focus(),!0}),n.bind("click",function(o){e.enableCellEditOnFocus&&(o.preventDefault(),i=!1,t(e,n))}),n.bind("focus",function(){return o=!0,e.enableCellEditOnFocus&&!i&&t(e,n),!0}),n.bind("blur",function(){return o=!1,!0}),n.bind("keydown",function(i){return e.enableCellEditOnFocus||(o&&37!==i.keyCode&&38!==i.keyCode&&39!==i.keyCode&&40!==i.keyCode&&9!==i.keyCode&&!i.shiftKey&&13!==i.keyCode&&t(e,n),o&&i.shiftKey&&i.keyCode>=65&&90>=i.keyCode&&t(e,n),27===i.keyCode&&n.focus()),!0})}}]),p.directive("ngCellText",function(){return function(e,t){t.bind("mouseover",function(e){e.preventDefault(),t.css({cursor:"text"})}),t.bind("mouseleave",function(e){e.preventDefault(),t.css({cursor:"default"})})}}),p.directive("ngCell",["$compile","$domUtilityService",function(e,t){var n={scope:!1,compile:function(){return{pre:function(t,n){var o,i=t.col.cellTemplate.replace(d,"row.entity."+t.col.field);t.col.enableCellEdit?(o=t.col.cellEditTemplate,o=o.replace(u,i),o=o.replace(f,t.col.editableCellTemplate.replace(d,"row.entity."+t.col.field))):o=i;var r=e(o)(t);t.enableCellSelection&&-1===r[0].className.indexOf("ngSelectionCell")&&(r[0].setAttribute("tabindex",0),r.addClass("ngCellElement")),n.append(r)},post:function(e,n){e.enableCellSelection&&e.domAccessProvider.selectionHandlers(e,n),e.$on("ngGridEventDigestCell",function(){t.digest(e)})}}}};return n}]),p.directive("ngEditCellIf",[function(){return{transclude:"element",priority:1e3,terminal:!0,restrict:"A",compile:function(e,t,n){return function(e,t,o){var i,r;e.$watch(o.ngEditCellIf,function(o){i&&(i.remove(),i=void 0),r&&(r.$destroy(),r=void 0),o&&(r=e.$new(),n(r,function(e){i=e,t.after(e)}))})}}}}]),p.directive("ngGridFooter",["$compile","$templateCache",function(e,t){var n={scope:!1,compile:function(){return{pre:function(n,o){0===o.children().length&&o.append(e(t.get(n.gridId+"footerTemplate.html"))(n))}}}};return n}]),p.directive("ngGridMenu",["$compile","$templateCache",function(e,t){var n={scope:!1,compile:function(){return{pre:function(n,o){0===o.children().length&&o.append(e(t.get(n.gridId+"menuTemplate.html"))(n))}}}};return n}]),p.directive("ngGrid",["$compile","$filter","$templateCache","$sortService","$domUtilityService","$utilityService","$timeout","$parse","$http","$q",function(e,n,o,i,r,l,a,s,c,g){var d={scope:!0,compile:function(){return{pre:function(d,u,f){var h=t(u),p=d.$eval(f.ngGrid);p.gridDim=new b({outerHeight:t(h).height(),outerWidth:t(h).width()});var m=new T(d,p,i,r,n,o,l,a,s,c,g);return m.init().then(function(){if("string"==typeof p.columnDefs?d.$parent.$watch(p.columnDefs,function(e){return e?(m.lateBoundColumns=!1,d.columns=[],m.config.columnDefs=e,m.buildColumns(),m.eventProvider.assignEvents(),r.RebuildGrid(d,m),void 0):(m.refreshDomSizes(),m.buildColumns(),void 0)},!0):m.buildColumns(),"string"==typeof p.totalServerItems?d.$parent.$watch(p.totalServerItems,function(e){d.totalServerItems=angular.isDefined(e)?e:0}):d.totalServerItems=0,"string"==typeof p.data){var n=function(e){m.data=t.extend([],e),m.rowFactory.fixRowCache(),angular.forEach(m.data,function(e,t){var n=m.rowMap[t]||t;m.rowCache[n]&&m.rowCache[n].ensureEntity(e),m.rowMap[n]=t}),m.searchProvider.evalFilter(),m.configureColumnWidths(),m.refreshDomSizes(),m.config.sortInfo.fields.length>0&&(m.sortColumnsInit(),d.$emit("ngGridEventSorted",m.config.sortInfo)),d.$emit("ngGridEventData",m.gridId)};d.$parent.$watch(p.data,n),d.$parent.$watch(p.data+".length",function(){n(d.$eval(p.data))})}return m.footerController=new y(d,m),u.addClass("ngGrid").addClass(""+m.gridId),p.enableHighlighting||u.addClass("unselectable"),p.jqueryUITheme&&u.addClass("ui-widget"),u.append(e(o.get("gridTemplate.html"))(d)),r.AssignGridContainers(d,u,m),m.eventProvider=new x(m,d,r,a),p.selectRow=function(e,t){m.rowCache[e]&&(m.rowCache[e].clone&&m.rowCache[e].clone.setSelection(t?!0:!1),m.rowCache[e].setSelection(t?!0:!1))},p.selectItem=function(e,t){p.selectRow(m.rowMap[e],t)},p.selectAll=function(e){d.toggleSelectAll(e)},p.selectVisible=function(e){d.toggleSelectAll(e,!0)},p.groupBy=function(e){if(e)d.groupBy(d.columns.filter(function(t){return t.field===e})[0]);else{var n=t.extend(!0,[],d.configGroups);angular.forEach(n,d.groupBy)}},p.sortBy=function(e){var t=d.columns.filter(function(t){return t.field===e})[0];t&&t.sort()},p.gridId=m.gridId,p.ngGrid=m,p.$gridScope=d,p.$gridServices={SortService:i,DomUtilityService:r,UtilityService:l},d.$on("ngGridEventDigestGrid",function(){r.digest(d.$parent)}),d.$on("ngGridEventDigestGridParent",function(){r.digest(d.$parent)}),d.$evalAsync(function(){d.adjustScrollLeft(0)}),angular.forEach(p.plugins,function(e){"function"==typeof e&&(e=new e),e.init(d.$new(),m,p.$gridServices),p.plugins[l.getInstanceType(e)]=e}),"function"==typeof p.init&&p.init(m,d),null})}}}};return d}]),p.directive("ngHeaderCell",["$compile",function(e){var t={scope:!1,compile:function(){return{pre:function(t,n){n.append(e(t.col.headerCellTemplate)(t))}}}};return t}]),p.directive("ngInput",[function(){return{require:"ngModel",link:function(e,t,n,o){var i,r=e.$watch("ngModel",function(){i=o.$modelValue,r()});t.bind("keydown",function(n){switch(n.keyCode){case 37:case 38:case 39:case 40:n.stopPropagation();break;case 27:e.$$phase||e.$apply(function(){o.$setViewValue(i),t.blur()});break;case 13:(e.enableCellEditOnFocus&&e.totalFilteredItemsLength()-1>e.row.rowIndex&&e.row.rowIndex>0||e.enableCellEdit)&&t.blur()}return!0}),t.bind("click",function(e){e.stopPropagation()}),t.bind("mousedown",function(e){e.stopPropagation()}),e.$on("ngGridEventStartCellEdit",function(){t.focus(),t.select()}),angular.element(t).bind("blur",function(){e.$emit("ngGridEventEndCellEdit")})}}}]),p.directive("ngRow",["$compile","$domUtilityService","$templateCache",function(e,t,n){var o={scope:!1,compile:function(){return{pre:function(o,i){if(o.row.elm=i,o.row.clone&&(o.row.clone.elm=i),o.row.isAggRow){var r=n.get(o.gridId+"aggregateTemplate.html");r=o.row.aggLabelFilter?r.replace(g,"| "+o.row.aggLabelFilter):r.replace(g,""),i.append(e(r)(o))}else i.append(e(n.get(o.gridId+"rowTemplate.html"))(o));o.$on("ngGridEventDigestRow",function(){t.digest(o)})}}}};return o}]),p.directive("ngViewport",[function(){return function(e,t){var n,o,i=0;t.bind("scroll",function(t){var r=t.target.scrollLeft,l=t.target.scrollTop;return e.$headerContainer&&e.$headerContainer.scrollLeft(r),e.adjustScrollLeft(r),e.adjustScrollTop(l),e.$root.$$phase||e.$digest(),o=r,i=l,n=!1,!0}),t.bind("mousewheel DOMMouseScroll",function(){return n=!0,t.focus&&t.focus(),!0}),e.enableCellSelection||e.domAccessProvider.selectionHandlers(e,t)}}]),e.ngGrid.i18n.da={ngAggregateLabel:"artikler",ngGroupPanelDescription:"Grupér rækker udfra en kolonne ved at trække dens overskift hertil.",ngSearchPlaceHolder:"Søg...",ngMenuText:"Vælg kolonner:",ngShowingItemsLabel:"Viste rækker:",ngTotalItemsLabel:"Rækker totalt:",ngSelectedItemsLabel:"Valgte rækker:",ngPageSizeLabel:"Side størrelse:",ngPagerFirstTitle:"Første side",ngPagerNextTitle:"Næste side",ngPagerPrevTitle:"Forrige side",ngPagerLastTitle:"Sidste side"},e.ngGrid.i18n.de={ngAggregateLabel:"artikel",ngGroupPanelDescription:"Ziehen Sie eine Spaltenüberschrift hier und legen Sie es der Gruppe nach dieser Spalte.",ngSearchPlaceHolder:"Suche...",ngMenuText:"Spalten auswählen:",ngShowingItemsLabel:"Zeige Artikel:",ngTotalItemsLabel:"Meiste Artikel:",ngSelectedItemsLabel:"Ausgewählte Artikel:",ngPageSizeLabel:"Größe Seite:",ngPagerFirstTitle:"Erste Page",ngPagerNextTitle:"Nächste Page",ngPagerPrevTitle:"Vorherige Page",ngPagerLastTitle:"Letzte Page"},e.ngGrid.i18n.en={ngAggregateLabel:"items",ngGroupPanelDescription:"Drag a column header here and drop it to group by that column.",ngSearchPlaceHolder:"Search...",ngMenuText:"Choose Columns:",ngShowingItemsLabel:"Showing Items:",ngTotalItemsLabel:"Total Items:",ngSelectedItemsLabel:"Selected Items:",ngPageSizeLabel:"Page Size:",ngPagerFirstTitle:"First Page",ngPagerNextTitle:"Next Page",ngPagerPrevTitle:"Previous Page",ngPagerLastTitle:"Last Page"},e.ngGrid.i18n.es={ngAggregateLabel:"Artículos",ngGroupPanelDescription:"Arrastre un encabezado de columna aquí y soltarlo para agrupar por esa columna.",ngSearchPlaceHolder:"Buscar...",ngMenuText:"Elegir columnas:",ngShowingItemsLabel:"Artículos Mostrando:",ngTotalItemsLabel:"Artículos Totales:",ngSelectedItemsLabel:"Artículos Seleccionados:",ngPageSizeLabel:"Tamaño de Página:",ngPagerFirstTitle:"Primera Página",ngPagerNextTitle:"Página Siguiente",ngPagerPrevTitle:"Página Anterior",ngPagerLastTitle:"Última Página"},e.ngGrid.i18n.fr={ngAggregateLabel:"articles",ngGroupPanelDescription:"Faites glisser un en-tête de colonne ici et déposez-le vers un groupe par cette colonne.",ngSearchPlaceHolder:"Recherche...",ngMenuText:"Choisir des colonnes:",ngShowingItemsLabel:"Articles Affichage des:",ngTotalItemsLabel:"Nombre total d'articles:",ngSelectedItemsLabel:"Éléments Articles:",ngPageSizeLabel:"Taille de page:",ngPagerFirstTitle:"Première page",ngPagerNextTitle:"Page Suivante",ngPagerPrevTitle:"Page précédente",ngPagerLastTitle:"Dernière page"},e.ngGrid.i18n["pt-br"]={ngAggregateLabel:"items",ngGroupPanelDescription:"Arraste e solte uma coluna aqui para agrupar por essa coluna",ngSearchPlaceHolder:"Procurar...",ngMenuText:"Selecione as colunas:",ngShowingItemsLabel:"Mostrando os Items:",ngTotalItemsLabel:"Total de Items:",ngSelectedItemsLabel:"Items Selecionados:",ngPageSizeLabel:"Tamanho da Página:",ngPagerFirstTitle:"Primeira Página",ngPagerNextTitle:"Próxima Página",ngPagerPrevTitle:"Página Anterior",ngPagerLastTitle:"Última Página"},e.ngGrid.i18n["zh-cn"]={ngAggregateLabel:"条目",ngGroupPanelDescription:"拖曳表头到此处以进行分组",ngSearchPlaceHolder:"搜索...",ngMenuText:"数据分组与选择列:",ngShowingItemsLabel:"当前显示条目:",ngTotalItemsLabel:"条目总数:",ngSelectedItemsLabel:"选中条目:",ngPageSizeLabel:"每页显示数:",ngPagerFirstTitle:"回到首页",ngPagerNextTitle:"下一页",ngPagerPrevTitle:"上一页",ngPagerLastTitle:"前往尾页"},e.ngGrid.i18n["zh-tw"]={ngAggregateLabel:"筆",ngGroupPanelDescription:"拖拉表頭到此處以進行分組",ngSearchPlaceHolder:"搜尋...",ngMenuText:"選擇欄位:",ngShowingItemsLabel:"目前顯示筆數:",ngTotalItemsLabel:"總筆數:",ngSelectedItemsLabel:"選取筆數:",ngPageSizeLabel:"每頁顯示:",ngPagerFirstTitle:"第一頁",ngPagerNextTitle:"下一頁",ngPagerPrevTitle:"上一頁",ngPagerLastTitle:"最後頁"},angular.module("ngGrid").run(["$templateCache",function(e){e.put("aggregateTemplate.html",'<div ng-click="row.toggleExpand()" ng-style="rowStyle(row)" class="ngAggregate"> <span class="ngAggregateText">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span> <div class="{{row.aggClass()}}"></div></div>'),e.put("cellEditTemplate.html",'<div ng-cell-has-focus ng-dblclick="editCell()"> <div ng-edit-cell-if="!isFocused"> DISPLAY_CELL_TEMPLATE </div> <div ng-edit-cell-if="isFocused"> EDITABLE_CELL_TEMPLATE </div></div>'),e.put("cellTemplate.html",'<div class="ngCellText" ng-class="col.colIndex()"><span ng-cell-text>{{COL_FIELD CUSTOM_FILTERS}}</span></div>'),e.put("checkboxCellTemplate.html",'<div class="ngSelectionCell"><input tabindex="-1" class="ngSelectionCheckbox" type="checkbox" ng-checked="row.selected" /></div>'),e.put("checkboxHeaderTemplate.html",'<input class="ngSelectionHeader" type="checkbox" ng-show="multiSelect" ng-model="allSelected" ng-change="toggleSelectAll(allSelected, true)"/>'),e.put("editableCellTemplate.html",'<input ng-class="\'colt\' + col.index" ng-input="COL_FIELD" ng-model="COL_FIELD" />'),e.put("footerTemplate.html",'<div ng-show="showFooter" class="ngFooterPanel" ng-class="{\'ui-widget-content\': jqueryUITheme, \'ui-corner-bottom\': jqueryUITheme}" ng-style="footerStyle()"> <div class="ngTotalSelectContainer" > <div class="ngFooterTotalItems" ng-class="{\'ngNoMultiSelect\': !multiSelect}" > <span class="ngLabel">{{i18n.ngTotalItemsLabel}} {{maxRows()}}</span><span ng-show="filterText.length > 0" class="ngLabel">({{i18n.ngShowingItemsLabel}} {{totalFilteredItemsLength()}})</span> </div> <div class="ngFooterSelectedItems" ng-show="multiSelect"> <span class="ngLabel">{{i18n.ngSelectedItemsLabel}} {{selectedItems.length}}</span> </div> </div> <div class="ngPagerContainer" style="float: right; margin-top: 10px;" ng-show="enablePaging" ng-class="{\'ngNoMultiSelect\': !multiSelect}"> <div style="float:left; margin-right: 10px;" class="ngRowCountPicker"> <span style="float: left; margin-top: 3px;" class="ngLabel">{{i18n.ngPageSizeLabel}}</span> <select style="float: left;height: 27px; width: 100px" ng-model="pagingOptions.pageSize" > <option ng-repeat="size in pagingOptions.pageSizes">{{size}}</option> </select> </div> <div style="float:left; margin-right: 10px; line-height:25px;" class="ngPagerControl" style="float: left; min-width: 135px;"> <button class="ngPagerButton" ng-click="pageToFirst()" ng-disabled="cantPageBackward()" title="{{i18n.ngPagerFirstTitle}}"><div class="ngPagerFirstTriangle"><div class="ngPagerFirstBar"></div></div></button> <button class="ngPagerButton" ng-click="pageBackward()" ng-disabled="cantPageBackward()" title="{{i18n.ngPagerPrevTitle}}"><div class="ngPagerFirstTriangle ngPagerPrevTriangle"></div></button> <input class="ngPagerCurrent" min="1" max="{{maxPages()}}" type="number" style="width:50px; height: 24px; margin-top: 1px; padding: 0 4px;" ng-model="pagingOptions.currentPage"/> <button class="ngPagerButton" ng-click="pageForward()" ng-disabled="cantPageForward()" title="{{i18n.ngPagerNextTitle}}"><div class="ngPagerLastTriangle ngPagerNextTriangle"></div></button> <button class="ngPagerButton" ng-click="pageToLast()" ng-disabled="cantPageToLast()" title="{{i18n.ngPagerLastTitle}}"><div class="ngPagerLastTriangle"><div class="ngPagerLastBar"></div></div></button> </div> </div></div>'),e.put("gridTemplate.html",'<div class="ngTopPanel" ng-class="{\'ui-widget-header\':jqueryUITheme, \'ui-corner-top\': jqueryUITheme}" ng-style="topPanelStyle()"> <div class="ngGroupPanel" ng-show="showGroupPanel()" ng-style="groupPanelStyle()"> <div class="ngGroupPanelDescription" ng-show="configGroups.length == 0">{{i18n.ngGroupPanelDescription}}</div> <ul ng-show="configGroups.length > 0" class="ngGroupList"> <li class="ngGroupItem" ng-repeat="group in configGroups"> <span class="ngGroupElement"> <span class="ngGroupName">{{group.displayName}} <span ng-click="removeGroup($index)" class="ngRemoveGroup">x</span> </span> <span ng-hide="$last" class="ngGroupArrow"></span> </span> </li> </ul> </div> <div class="ngHeaderContainer" ng-style="headerStyle()"> <div class="ngHeaderScroller" ng-style="headerScrollerStyle()" ng-include="gridId + \'headerRowTemplate.html\'"></div> </div> <div ng-grid-menu></div></div><div class="ngViewport" unselectable="on" ng-viewport ng-class="{\'ui-widget-content\': jqueryUITheme}" ng-style="viewportStyle()"> <div class="ngCanvas" ng-style="canvasStyle()"> <div ng-style="rowStyle(row)" ng-repeat="row in renderedRows" ng-click="row.toggleSelected($event)" ng-class="row.alternatingRowClass()" ng-row></div> </div></div><div ng-grid-footer></div>'),e.put("headerCellTemplate.html",'<div class="ngHeaderSortColumn {{col.headerClass}}" ng-style="{\'cursor\': col.cursor}" ng-class="{ \'ngSorted\': !noSortVisible }"> <div ng-click="col.sort($event)" ng-class="\'colt\' + col.index" class="ngHeaderText">{{col.displayName}}</div> <div class="ngSortButtonDown" ng-show="col.showSortButtonDown()"></div> <div class="ngSortButtonUp" ng-show="col.showSortButtonUp()"></div> <div class="ngSortPriority">{{col.sortPriority}}</div> <div ng-class="{ ngPinnedIcon: col.pinned, ngUnPinnedIcon: !col.pinned }" ng-click="togglePin(col)" ng-show="col.pinnable"></div></div><div ng-show="col.resizable" class="ngHeaderGrip" ng-click="col.gripClick($event)" ng-mousedown="col.gripOnMouseDown($event)"></div>'),e.put("headerRowTemplate.html",'<div ng-style="{ height: col.headerRowHeight }" ng-repeat="col in renderedColumns" ng-class="col.colIndex()" class="ngHeaderCell"> <div class="ngVerticalBar" ng-style="{height: col.headerRowHeight}" ng-class="{ ngVerticalBarVisible: !$last }"> </div> <div ng-header-cell></div></div>'),e.put("menuTemplate.html",'<div ng-show="showColumnMenu || showFilter" class="ngHeaderButton" ng-click="toggleShowMenu()"> <div class="ngHeaderButtonArrow"></div></div><div ng-show="showMenu" class="ngColMenu"> <div ng-show="showFilter"> <input placeholder="{{i18n.ngSearchPlaceHolder}}" type="text" ng-model="filterText"/> </div> <div ng-show="showColumnMenu"> <span class="ngMenuText">{{i18n.ngMenuText}}</span> <ul class="ngColList"> <li class="ngColListItem" ng-repeat="col in columns | ngColumns"> <label><input ng-disabled="col.pinned" type="checkbox" class="ngColListCheckbox" ng-model="col.visible"/>{{col.displayName}}</label> <a title="Group By" ng-class="col.groupedByClass()" ng-show="col.groupable && col.visible" ng-click="groupBy(col)"></a> <span class="ngGroupingNumber" ng-show="col.groupIndex > 0">{{col.groupIndex}}</span> </li> </ul> </div></div>'),e.put("rowTemplate.html",'<div ng-style="{ \'cursor\': row.cursor }" ng-repeat="col in renderedColumns" ng-class="col.colIndex()" class="ngCell {{col.cellClass}}"> <div class="ngVerticalBar" ng-style="{height: rowHeight}" ng-class="{ ngVerticalBarVisible: !$last }"> </div> <div ng-cell></div></div>')}])})(window,jQuery); \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/ng-select2.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/ng-select2.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/ng-select2.js 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,160 @@ +/** + * Enhanced Select2 Dropmenus + * + * @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2 + * This change is so that you do not have to do an additional query yourself on top of Select2's own query + * @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation + */ +angular.module('ui.select2', []).value('uiSelect2Config', {}).directive('uiSelect2', ['uiSelect2Config', '$timeout', function (uiSelect2Config, $timeout) { + var options = {}; + if (uiSelect2Config) { + angular.extend(options, uiSelect2Config); + } + return { + require: 'ngModel', + compile: function (tElm, tAttrs) { + var watch, + repeatOption, + repeatAttr, + isSelect = tElm.is('select'), + isMultiple = (tAttrs.multiple !== undefined); + + // Enable watching of the options dataset if in use + if (tElm.is('select')) { + repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]'); + + if (repeatOption.length) { + repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat'); + watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop(); + } + } + + return function (scope, elm, attrs, controller) { + // instance-specific options + var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2)); + + if (isSelect) { + // Use <select multiple> instead + delete opts.multiple; + delete opts.initSelection; + } else if (isMultiple) { + opts.multiple = true; + } + + if (controller) { + // Watch the model for programmatic changes + scope.$watch(tAttrs.ngModel, function(current, old) { + if (!current) { + return + } + if (current == old) { + return + } + controller.$render() + }, true) + controller.$render = function () { + if (isSelect) { + elm.select2('val', controller.$viewValue); + } else { + if (isMultiple) { + if (!controller.$viewValue) { + elm.select2('data', []); + } else if (angular.isArray(controller.$viewValue)) { + elm.select2('data', controller.$viewValue); + } else { + elm.select2('val', controller.$viewValue); + } + } else { + if (angular.isObject(controller.$viewValue)) { + elm.select2('data', controller.$viewValue); + } else if (!controller.$viewValue) { + elm.select2('data', null); + } else { + elm.select2('val', controller.$viewValue); + } + } + } + }; + + // Watch the options dataset for changes + if (watch) { + scope.$watch(watch, function (newVal, oldVal, scope) { + if (!newVal) return; + // Delayed so that the options have time to be rendered + $timeout(function () { + elm.select2('val', controller.$viewValue); + // Refresh angular to remove the superfluous option + elm.trigger('change'); + }); + }); + } + + // Update valid and dirty statuses + controller.$parsers.push(function (value) { + var div = elm.prev() + div + .toggleClass('ng-invalid', !controller.$valid) + .toggleClass('ng-valid', controller.$valid) + .toggleClass('ng-invalid-required', !controller.$valid) + .toggleClass('ng-valid-required', controller.$valid) + .toggleClass('ng-dirty', controller.$dirty) + .toggleClass('ng-pristine', controller.$pristine); + return value; + }); + + if (!isSelect) { + // Set the view and model value and update the angular template manually for the ajax/multiple select2. + elm.bind("change", function () { + if (scope.$$phase) return; + scope.$apply(function () { + controller.$setViewValue(elm.select2('data')); + }); + }); + + if (opts.initSelection) { + var initSelection = opts.initSelection; + opts.initSelection = function (element, callback) { + initSelection(element, function (value) { + controller.$setViewValue(value); + callback(value); + }); + }; + } + } + } + + elm.bind("$destroy", function() { + elm.select2("destroy"); + }); + + attrs.$observe('disabled', function (value) { + elm.select2('enable', !value); + }); + + attrs.$observe('readonly', function (value) { + elm.select2('readonly', !!value); + }); + + if (attrs.ngMultiple) { + scope.$watch(attrs.ngMultiple, function(newVal) { + elm.select2(opts); + }); + } + + // Initialize the plugin late so that the injected DOM does not disrupt the template compiler + $timeout(function () { + elm.select2(opts); + + // Set initial value - I'm not sure about this but it seems to need to be there + elm.val(controller.$viewValue); + // important! + controller.$render(); + + // Not sure if I should just check for !isSelect OR if I should check for 'tags' key + if (!opts.initSelection && !isSelect) + controller.$setViewValue(elm.select2('data')); + }); + }; + } + }; +}]); Added: trunk/chorem-webmotion/src/main/webapp/js/ng-wikitty.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/ng-wikitty.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/ng-wikitty.js 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,295 @@ +var wikitty = angular.module('wikitty', ['ngResource', 'ngGrid', 'ui.select2']); + +wikitty.factory('Wikitty', function($resource){ + var result = $resource(webContext + 'rest/wikitty/:id', {id:'@meta.id'}, + { 'getNoCache': {method:'GET'}, + 'queryNoCache': {method:'GET', isArray:true}, + 'save': {method:'POST'}, + 'delete': {method:'DELETE'} } + ); + + /** + * Cache commun a toutes les instances, permet de mutualise les informations + * recupere depuis le serveur via des 'get' ou des 'query' + */ + result.cache = { + wikitty: {}, + extension: {} + }; + + /** + * Retourne le cache utilise par toutes les instances + * @return {Object} + */ + result.prototype.getCache = function() { + return result.cache; + } + + result.cacheWikitty = function(wikitty) { + if (angular.isArray(wikitty)) { + angular.forEach(wikitty, function(w) { + result.cacheWikitty(w); + }); + } else { + result.cache.wikitty[wikitty.meta.id] = new result(wikitty); + } + } + + result.cacheExtension = function(extension) { + if (angular.isArray(extension)) { + angular.forEach(extension, function(e) { + result.cacheExtension(e) + }); + } else { + result.cache.extension[extension.name] = extension; + } + } + + /** + * Permet de cree un tableau contenant le nom de l'extension et le nom du champs + * @param {String} extensionOrFqFiel extension name or fq field + * @param {String} field can be undefine + * @return {unresolved} + */ + result.prototype.__getFq__ = function(extensionOrFqFiel, field) { + var fq; + if (angular.isArray(extensionOrFqFiel)) { + fq = extensionOrFqFiel; + } else { + fq = extensionOrFqFiel.split("."); + } + if (fq.length < 2 && field) { + fq.push(field); + } + return fq; + }; + + result.prototype.getId = function () { + return this.meta.id; + }; + + /** + * Permet de retourne une representation humaine: + * <li> d'un wikitty si pas d'argument + * <li> d'un extension du wikitty sur seulement un nom d'extension en parametre + * <li> d'un champs si un champs fq en parametre + * @param {String} extensionOrFqFiel + * @param {String} field + * @return {String} + */ + result.prototype.display = function (extensionOrFqFiel, field) { + var result = 'error'; + if (extensionOrFqFiel) { + var fq = this.__getFq__(extensionOrFqFiel, field); + + if (fq.length > 1) { + var fieldInfo = this.getFieldInfo(fq); + var value = this.getField(fq); + if (value && fieldInfo && fieldInfo.type === 'WIKITTY') { + result = this.getCache().wikitty[value].display(); + } else { + result = value; + } + } else { + result = this.meta.displayString[fq[0]]; + } + } else { + result = this.meta.displayString[""]; + } + return result; + }; + + result.prototype.getWikitty = function(id, callback) { + var w = this.getCache().wikitty[id]; + if (w) { + if (callback) { + callback(w); + } else { + return w; + } + } else { + result.get(id, callback); + } + }; + + /** + * Recupere un wikitty sur le serveur et met en cache les informations + * + * @param {String} id + * @param {function} callback + * @return {undefine} + */ + result.get = function(id, callback) { + var params = id; + if (!angular.isObject(id)) { + params = {id:id}; + } + this.getNoCache(params, function(data) { + result.cache.wikitty[data.meta.id] = data; + angular.forEach(data.cache.wikitty, function (w, id) { + result.cacheWikitty(w); + }); + angular.forEach(data.cache.extension, function (e, name) { + result.cacheExtension(e); + }); + + delete data.cache.wikitty; + delete data.cache.extension; + + if (callback) { + callback(data); + } + }); + }; + + /** + * Recupere sur le serveur un ensemble d'objet qui satisfont une requete + * @param {String|Object|Array of id} query la query a utiliser pour selectionner les objets ou un objet contenant les parametres + * @param {function} callback le callback a appeler une fois les donnees recuperees + * @return {undefined} + */ + result.query = function(query, callback) { + var params; + if (angular.isArray(query)) { + var q = "id="+query.join(" OR id="); + params = {q:q}; + } else if (!angular.isObject(query)) { + params = {q:query}; + } else { + params = query; + } + this.queryNoCache(params, function(data) { + angular.forEach(data, function(v) { + result.cache.wikitty[v.meta.id] = v; + angular.extend(result.cache.wikitty, v.cache.wikitty); + angular.extend(result.cache.extension, v.cache.extension); + + delete v.cache.wikitty; + delete v.cache.extension; + }); + if (callback) { + callback(data); + } + }); + }; + + /** + * recupere les noms des extensions utilises par ce wikitty + * @param {String} extensionOrFqFiel + * @return {Object} + */ + result.prototype.getExtensionNames = function() { + var result = this.meta.extensions; + return result; + }; + + /** + * recupere une extension + * @param {String} extensionOrFqField + * @return {Object} + */ + result.prototype.getExtension = function(extensionOrFqField) { + var fq = this.__getFq__(extensionOrFqField); + var result = this.getCache().extension[fq[0]]; + return result; + }; + + /** + * Donne le nom des champs d'une extension + * @param {String} extensionOrFqField + * @return {Array of String} + */ + result.prototype.getExtensionFieldNames = function(extensionOrFqField) { + var fq = this.__getFq__(extensionOrFqField); + var result = Object.keys(this.getExtension(fq[0]).fields); + return result; + }; + + /** + * Donne le nom des champs de toutes les extensions du wikitty + * @return {Array of String} + */ + result.prototype.getFieldNames = function() { + var result = []; + angular.forEach(this.getExtensionNames(), function(extName) { + angular.extend(result, this.getExtensionFieldNames(extName)); + }); + return result; + }; + + /** + * Recupere les informations sur un champs + * @param {String} extensionOrFqField + * @param {String} field + * @return {Object} + */ + result.prototype.getFieldInfo = function(extensionOrFqField, field) { + var result; + var fq = this.__getFq__(extensionOrFqField, field); + var ext = this.getExtension(fq[0]); + if (ext && fq.length > 1) { + result = ext.fields[fq[1]]; + } + return result; + }; + + /** + * Recupere la valeur d'un champs + * @param {String} extensionOrFqField + * @param {String} field + * @return {Object} + */ + result.prototype.getField = function(extensionOrFqField, field) { + var fq = this.__getFq__(extensionOrFqField, field); + + var result = this.data[fq[0] + "." + fq[1]]; + if (result && angular.isString(result)) { + var fieldInfo = this.getFieldInfo(fq); + if (fieldInfo.type === 'DATE') { + result = new Date(result); + } + } + return result; + }; + + return result; +}); + +wikitty.factory('Select', function($http) { + var result = function(query, callback) { + $http.get(webContext + 'rest/select', {params:{q:query}}).success(callback); + }; + return result; +}); + +wikitty.factory('Facet', function($http) { + var result = function(query, facetField, facetQuery, callback) { + var filter = query; + if (filter == "") { + filter = "*:*"; + } + + var ff = ""; + var ffs = ""; + if (angular.isObject(facetField)) { + var sep = "?" + for (var field in facetField) { + ffs += sep + "ff=" + field; + sep = "&"; + } + + } else if (angular.isArray(facetField)) { + var sep = "?" + for (var i=0; i<facetField.length; i++) { + var field = facetField[i]; + ffs += sep + "ff=" + field; + sep = "&"; + } + } else { + ff = facetField; + } + + $http.get(webContext + 'rest/facet' + ffs, {params:{q:filter, ff:ff, fq:facetQuery}}).success(callback); + }; + return result; +}); Added: trunk/chorem-webmotion/src/main/webapp/js/select2.min.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/select2.min.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/select2.min.js 2013-07-19 13:50:40 UTC (rev 370) @@ -0,0 +1,22 @@ +/* +Copyright 2012 Igor Vaynberg + +Version: 3.4.1 Timestamp: Thu Jun 27 18:02:10 PDT 2013 + +This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU +General Public License version 2 (the "GPL License"). You may choose either license to govern your +use of this software only upon the condition that you accept all of the terms of either the Apache +License or the GPL License. + +You may obtain a copy of the Apache License and the GPL License at: + +http://www.apache.org/licenses/LICENSE-2.0 +http://www.gnu.org/licenses/gpl-2.0.html + +Unless required by applicable law or agreed to in writing, software distributed under the Apache License +or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See the Apache License and the GPL License for the specific language governing +permissions and limitations under the Apache License and the GPL License. +*/ +(function(a){a.fn.each2===void 0&&a.fn.extend({each2:function(b){for(var c=a([0]),d=-1,e=this.length;e>++d&&(c.context=c[0]=this[d])&&b.call(c[0],d,c)!==!1;);return this}})})(jQuery),function(a,b){"use strict";function m(a,b){for(var c=0,d=b.length;d>c;c+=1)if(o(a,b[c]))return c;return-1}function n(){var b=a(l);b.appendTo("body");var c={width:b.width()-b[0].clientWidth,height:b.height()-b[0].clientHeight};return b.remove(),c}function o(a,c){return a===c?!0:a===b||c===b?!1:null===a||null===c?!1:a.constructor===String?a+""==c+"":c.constructor===String?c+""==a+"":!1}function p(b,c){var d,e,f;if(null===b||1>b.length)return[];for(d=b.split(c),e=0,f=d.length;f>e;e+=1)d[e]=a.trim(d[e]);return d}function q(a){return a.outerWidth(!1)-a.width()}function r(c){var d="keyup-change-value";c.on("keydown",function(){a.data(c,d)===b&&a.data(c,d,c.val())}),c.on("keyup",function(){var e=a.data(c,d);e!==b&&c.val()!==e&&(a.removeData(c,d),c.trigger("keyup-change"))})}function s(c){c.on("mousemove",function(c){var d=i;(d===b||d.x!==c.pageX||d.y!==c.pageY)&&a(c.target).trigger("mousemove-filtered",c)})}function t(a,c,d){d=d||b;var e;return function(){var b=arguments;window.clearTimeout(e),e=window.setTimeout(function(){c.apply(d,b)},a)}}function u(a){var c,b=!1;return function(){return b===!1&&(c=a(),b=!0),c}}function v(a,b){var c=t(a,function(a){b.trigger("scroll-debounced",a)});b.on("scroll",function(a){m(a.target,b.get())>=0&&c(a)})}function w(a){a[0]!==document.activeElement&&window.setTimeout(function(){var d,b=a[0],c=a.val().length;a.focus(),a.is(":visible")&&b===document.activeElement&&(b.setSelectionRange?b.setSelectionRange(c,c):b.createTextRange&&(d=b.createTextRange(),d.collapse(!1),d.select()))},0)}function x(b){b=a(b)[0];var c=0,d=0;if("selectionStart"in b)c=b.selectionStart,d=b.selectionEnd-c;else if("selection"in document){b.focus();var e=document.selection.createRange();d=document.selection.createRange().text.length,e.moveStart("character",-b.value.length),c=e.text.length-d}return{offset:c,length:d}}function y(a){a.preventDefault(),a.stopPropagation()}function z(a){a.preventDefault(),a.stopImmediatePropagation()}function A(b){if(!h){var c=b[0].currentStyle||window.getComputedStyle(b[0],null);h=a(document.createElement("div")).css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),h.attr("class","select2-sizer"),a("body").append(h)}return h.text(b.val()),h.width()}function B(b,c,d){var e,g,f=[];e=b.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0===this.indexOf("select2-")&&f.push(this)})),e=c.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0!==this.indexOf("select2-")&&(g=d(this),g&&f.push(this))})),b.attr("class",f.join(" "))}function C(a,c,d,e){var f=a.toUpperCase().indexOf(c.toUpperCase()),g=c.length;return 0>f?(d.push(e(a)),b):(d.push(e(a.substring(0,f))),d.push("<span class='select2-match'>"),d.push(e(a.substring(f,f+g))),d.push("</span>"),d.push(e(a.substring(f+g,a.length))),b)}function D(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return(a+"").replace(/[&<>"'\/\\]/g,function(a){return b[a]})}function E(c){var d,e=0,f=null,g=c.quietMillis||100,h=c.url,i=this;return function(j){window.clearTimeout(d),d=window.setTimeout(function(){e+=1;var d=e,g=c.data,k=h,l=c.transport||a.fn.select2.ajaxDefaults.transport,m={type:c.type||"GET",cache:c.cache||!1,jsonpCallback:c.jsonpCallback||b,dataType:c.dataType||"json"},n=a.extend({},a.fn.select2.ajaxDefaults.params,m);g=g?g.call(i,j.term,j.page,j.context):null,k="function"==typeof k?k.call(i,j.term,j.page,j.context):k,f&&f.abort(),c.params&&(a.isFunction(c.params)?a.extend(n,c.params.call(i)):a.extend(n,c.params)),a.extend(n,{url:k,dataType:c.dataType,data:g,success:function(a){if(!(e>d)){var b=c.results(a,j.page);j.callback(b)}}}),f=l.call(i,n)},g)}}function F(c){var e,f,d=c,g=function(a){return""+a.text};a.isArray(d)&&(f=d,d={results:f}),a.isFunction(d)===!1&&(f=d,d=function(){return f});var h=d();return h.text&&(g=h.text,a.isFunction(g)||(e=h.text,g=function(a){return a[e]})),function(c){var h,e=c.term,f={results:[]};return""===e?(c.callback(d()),b):(h=function(b,d){var f,i;if(b=b[0],b.children){f={};for(i in b)b.hasOwnProperty(i)&&(f[i]=b[i]);f.children=[],a(b.children).each2(function(a,b){h(b,f.children)}),(f.children.length||c.matcher(e,g(f),b))&&d.push(f)}else c.matcher(e,g(b),b)&&d.push(b)},a(d().results).each2(function(a,b){h(b,f.results)}),c.callback(f),b)}}function G(c){var d=a.isFunction(c);return function(e){var f=e.term,g={results:[]};a(d?c():c).each(function(){var a=this.text!==b,c=a?this.text:this;(""===f||e.matcher(f,c))&&g.results.push(a?this:{id:this,text:this})}),e.callback(g)}}function H(b,c){if(a.isFunction(b))return!0;if(!b)return!1;throw Error(c+" must be a function or a falsy value")}function I(b){return a.isFunction(b)?b():b}function J(b){var c=0;return a.each(b,function(a,b){b.children?c+=J(b.children):c++}),c}function K(a,c,d,e){var h,i,j,k,l,f=a,g=!1;if(!e.createSearchChoice||!e.tokenSeparators||1>e.tokenSeparators.length)return b;for(;;){for(i=-1,j=0,k=e.tokenSeparators.length;k>j&&(l=e.tokenSeparators[j],i=a.indexOf(l),!(i>=0));j++);if(0>i)break;if(h=a.substring(0,i),a=a.substring(i+l.length),h.length>0&&(h=e.createSearchChoice.call(this,h,c),h!==b&&null!==h&&e.id(h)!==b&&null!==e.id(h))){for(g=!1,j=0,k=c.length;k>j;j++)if(o(e.id(h),e.id(c[j]))){g=!0;break}g||d(h)}}return f!==a?a:b}function L(b,c){var d=function(){};return d.prototype=new b,d.prototype.constructor=d,d.prototype.parent=b.prototype,d.prototype=a.extend(d.prototype,c),d}if(window.Select2===b){var c,d,e,f,g,h,j,k,i={x:0,y:0},c={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case c.LEFT:case c.RIGHT:case c.UP:case c.DOWN:return!0}return!1},isControl:function(a){var b=a.which;switch(b){case c.SHIFT:case c.CTRL:case c.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){return a=a.which?a.which:a,a>=112&&123>=a}},l="<div class='select2-measure-scrollbar'></div>";j=a(document),g=function(){var a=1;return function(){return a++}}(),j.on("mousemove",function(a){i.x=a.pageX,i.y=a.pageY}),d=L(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(c){var d,e,h,i,f=".select2-results";this.opts=c=this.prepareOpts(c),this.id=c.id,c.element.data("select2")!==b&&null!==c.element.data("select2")&&c.element.data("select2").destroy(),this.container=this.createContainer(),this.containerId="s2id_"+(c.element.attr("id")||"autogen"+g()),this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1"),this.container.attr("id",this.containerId),this.body=u(function(){return c.element.closest("body")}),B(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.css(I(c.containerCss)),this.container.addClass(I(c.containerCssClass)),this.elementTabIndex=this.opts.element.attr("tabindex"),this.opts.element.data("select2",this).attr("tabindex","-1").before(this.container),this.container.data("select2",this),this.dropdown=this.container.find(".select2-drop"),this.dropdown.addClass(I(c.dropdownCssClass)),this.dropdown.data("select2",this),this.results=d=this.container.find(f),this.search=e=this.container.find("input.select2-input"),this.resultsPage=0,this.context=null,this.initContainer(),s(this.results),this.dropdown.on("mousemove-filtered touchstart touchmove touchend",f,this.bind(this.highlightUnderEvent)),v(80,this.results),this.dropdown.on("scroll-debounced",f,this.bind(this.loadMoreIfNeeded)),a(this.container).on("change",".select2-input",function(a){a.stopPropagation()}),a(this.dropdown).on("change",".select2-input",function(a){a.stopPropagation()}),a.fn.mousewheel&&d.mousewheel(function(a,b,c,e){var f=d.scrollTop();e>0&&0>=f-e?(d.scrollTop(0),y(a)):0>e&&d.get(0).scrollHeight-d.scrollTop()+e<=d.height()&&(d.scrollTop(d.get(0).scrollHeight-d.height()),y(a))}),r(e),e.on("keyup-change input paste",this.bind(this.updateResults)),e.on("focus",function(){e.addClass("select2-focused")}),e.on("blur",function(){e.removeClass("select2-focused")}),this.dropdown.on("mouseup",f,this.bind(function(b){a(b.target).closest(".select2-result-selectable").length>0&&(this.highlightUnderEvent(b),this.selectHighlighted(b))})),this.dropdown.on("click mouseup mousedown",function(a){a.stopPropagation()}),a.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource()),null!==c.maximumInputLength&&this.search.attr("maxlength",c.maximumInputLength);var h=c.element.prop("disabled");h===b&&(h=!1),this.enable(!h);var i=c.element.prop("readonly");i===b&&(i=!1),this.readonly(i),k=k||n(),this.autofocus=c.element.prop("autofocus"),c.element.prop("autofocus",!1),this.autofocus&&this.focus()},destroy:function(){var a=this.opts.element,c=a.data("select2");this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),c!==b&&(c.container.remove(),c.dropdown.remove(),a.removeClass("select2-offscreen").removeData("select2").off(".select2").prop("autofocus",this.autofocus||!1),this.elementTabIndex?a.attr({tabindex:this.elementTabIndex}):a.removeAttr("tabindex"),a.show())},optionToData:function(a){return a.is("option")?{id:a.prop("value"),text:a.text(),element:a.get(),css:a.attr("class"),disabled:a.prop("disabled"),locked:o(a.attr("locked"),"locked")||o(a.data("locked"),!0)}:a.is("optgroup")?{text:a.attr("label"),children:[],element:a.get(),css:a.attr("class")}:b},prepareOpts:function(c){var d,e,f,g,h=this;if(d=c.element,"select"===d.get(0).tagName.toLowerCase()&&(this.select=e=c.element),e&&a.each(["id","multiple","ajax","query","createSearchChoice","initSelection","data","tags"],function(){if(this in c)throw Error("Option '"+this+"' is not allowed for Select2 when attached to a <select> element.")}),c=a.extend({},{populateResults:function(d,e,f){var g,l=this.opts.id;g=function(d,e,i){var j,k,m,n,o,p,q,r,s,t;for(d=c.sortResults(d,e,f),j=0,k=d.length;k>j;j+=1)m=d[j],o=m.disabled===!0,n=!o&&l(m)!==b,p=m.children&&m.children.length>0,q=a("<li></li>"),q.addClass("select2-results-dept-"+i),q.addClass("select2-result"),q.addClass(n?"select2-result-selectable":"select2-result-unselectable"),o&&q.addClass("select2-disabled"),p&&q.addClass("select2-result-with-children"),q.addClass(h.opts.formatResultCssClass(m)),r=a(document.createElement("div")),r.addClass("select2-result-label"),t=c.formatResult(m,r,f,h.opts.escapeMarkup),t!==b&&r.html(t),q.append(r),p&&(s=a("<ul></ul>"),s.addClass("select2-result-sub"),g(m.children,s,i+1),q.append(s)),q.data("select2-data",m),e.append(q)},g(e,d,0)}},a.fn.select2.defaults,c),"function"!=typeof c.id&&(f=c.id,c.id=function(a){return a[f]}),a.isArray(c.element.data("select2Tags"))){if("tags"in c)throw"tags specified as both an attribute 'data-select2-tags' and in options of Select2 "+c.element.attr("id");c.tags=c.element.data("select2Tags")}if(e?(c.query=this.bind(function(a){var f,g,i,c={results:[],more:!1},e=a.term;i=function(b,c){var d;b.is("option")?a.matcher(e,b.text(),b)&&c.push(h.optionToData(b)):b.is("optgroup")&&(d=h.optionToData(b),b.children().each2(function(a,b){i(b,d.children)}),d.children.length>0&&c.push(d))},f=d.children(),this.getPlaceholder()!==b&&f.length>0&&(g=this.getPlaceholderOption(),g&&(f=f.not(g))),f.each2(function(a,b){i(b,c.results)}),a.callback(c)}),c.id=function(a){return a.id},c.formatResultCssClass=function(a){return a.css}):"query"in c||("ajax"in c?(g=c.element.data("ajax-url"),g&&g.length>0&&(c.ajax.url=g),c.query=E.call(c.element,c.ajax)):"data"in c?c.query=F(c.data):"tags"in c&&(c.query=G(c.tags),c.createSearchChoice===b&&(c.createSearchChoice=function(a){return{id:a,text:a}}),c.initSelection===b&&(c.initSelection=function(d,e){var f=[];a(p(d.val(),c.separator)).each(function(){var d=this,e=this,g=c.tags;a.isFunction(g)&&(g=g()),a(g).each(function(){return o(this.id,d)?(e=this.text,!1):b}),f.push({id:d,text:e})}),e(f)}))),"function"!=typeof c.query)throw"query function not defined for Select2 "+c.element.attr("id");return c},monitorSource:function(){var c,a=this.opts.element;a.on("change.select2",this.bind(function(){this.opts.element.data("select2-change-triggered")!==!0&&this.initSelection()})),c=this.bind(function(){var d,f=a.prop("disabled");f===b&&(f=!1),this.enable(!f);var d=a.prop("readonly");d===b&&(d=!1),this.readonly(d),B(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.addClass(I(this.opts.containerCssClass)),B(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(I(this.opts.dropdownCssClass))}),a.on("propertychange.select2 DOMAttrModified.select2",c),this.mutationCallback===b&&(this.mutationCallback=function(a){a.forEach(c)}),"undefined"!=typeof WebKitMutationObserver&&(this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),this.propertyObserver=new WebKitMutationObserver(this.mutationCallback),this.propertyObserver.observe(a.get(0),{attributes:!0,subtree:!1}))},triggerSelect:function(b){var c=a.Event("select2-selecting",{val:this.id(b),object:b});return this.opts.element.trigger(c),!c.isDefaultPrevented()},triggerChange:function(b){b=b||{},b=a.extend({},b,{type:"change",val:this.val()}),this.opts.element.data("select2-change-triggered",!0),this.opts.element.trigger(b),this.opts.element.data("select2-change-triggered",!1),this.opts.element.click(),this.opts.blurOnChange&&this.opts.element.blur()},isInterfaceEnabled:function(){return this.enabledInterface===!0},enableInterface:function(){var a=this._enabled&&!this._readonly,b=!a;return a===this.enabledInterface?!1:(this.container.toggleClass("select2-container-disabled",b),this.close(),this.enabledInterface=a,!0)},enable:function(a){return a===b&&(a=!0),this._enabled===a?!1:(this._enabled=a,this.opts.element.prop("disabled",!a),this.enableInterface(),!0)},readonly:function(a){return a===b&&(a=!1),this._readonly===a?!1:(this._readonly=a,this.opts.element.prop("readonly",a),this.enableInterface(),!0)},opened:function(){return this.container.hasClass("select2-dropdown-open")},positionDropdown:function(){var q,r,s,t,b=this.dropdown,c=this.container.offset(),d=this.container.outerHeight(!1),e=this.container.outerWidth(!1),f=b.outerHeight(!1),g=a(window).scrollLeft()+a(window).width(),h=a(window).scrollTop()+a(window).height(),i=c.top+d,j=c.left,l=h>=i+f,m=c.top-f>=this.body().scrollTop(),n=b.outerWidth(!1),o=g>=j+n,p=b.hasClass("select2-drop-above");this.opts.dropdownAutoWidth?(t=a(".select2-results",b)[0],b.addClass("select2-drop-auto-width"),b.css("width",""),n=b.outerWidth(!1)+(t.scrollHeight===t.clientHeight?0:k.width),n>e?e=n:n=e,o=g>=j+n):this.container.removeClass("select2-drop-auto-width"),"static"!==this.body().css("position")&&(q=this.body().offset(),i-=q.top,j-=q.left),p?(r=!0,!m&&l&&(r=!1)):(r=!1,!l&&m&&(r=!0)),o||(j=c.left+e-n),r?(i=c.top-f,this.container.addClass("select2-drop-above"),b.addClass("select2-drop-above")):(this.container.removeClass("select2-drop-above"),b.removeClass("select2-drop-above")),s=a.extend({top:i,left:j,width:e},I(this.opts.dropdownCss)),b.css(s)},shouldOpen:function(){var b;return this.opened()?!1:this._enabled===!1||this._readonly===!0?!1:(b=a.Event("select2-opening"),this.opts.element.trigger(b),!b.isDefaultPrevented())},clearDropdownAlignmentPreference:function(){this.container.removeClass("select2-drop-above"),this.dropdown.removeClass("select2-drop-above")},open:function(){return this.shouldOpen()?(this.opening(),!0):!1},opening:function(){function i(){return{width:Math.max(document.documentElement.scrollWidth,a(window).width()),height:Math.max(document.documentElement.scrollHeight,a(window).height())}}var f,g,b=this.containerId,c="scroll."+b,d="resize."+b,e="orientationchange."+b;this.container.addClass("select2-dropdown-open").addClass("select2-container-active"),this.clearDropdownAlignmentPreference(),this.dropdown[0]!==this.body().children().last()[0]&&this.dropdown.detach().appendTo(this.body()),f=a("#select2-drop-mask"),0==f.length&&(f=a(document.createElement("div")),f.attr("id","select2-drop-mask").attr("class","select2-drop-mask"),f.hide(),f.appendTo(this.body()),f.on("mousedown touchstart click",function(b){var d,c=a("#select2-drop");c.length>0&&(d=c.data("select2"),d.opts.selectOnBlur&&d.selectHighlighted({noFocus:!0}),d.close(),b.preventDefault(),b.stopPropagation())})),this.dropdown.prev()[0]!==f[0]&&this.dropdown.before(f),a("#select2-drop").removeAttr("id"),this.dropdown.attr("id","select2-drop"),g=i(),f.css(g).show(),this.dropdown.show(),this.positionDropdown(),this.dropdown.addClass("select2-drop-active");var h=this;this.container.parents().add(window).each(function(){a(this).on(d+" "+c+" "+e,function(){var c=i();a("#select2-drop-mask").css(c),h.positionDropdown()})})},close:function(){if(this.opened()){var b=this.containerId,c="scroll."+b,d="resize."+b,e="orientationchange."+b;this.container.parents().add(window).each(function(){a(this).off(c).off(d).off(e)}),this.clearDropdownAlignmentPreference(),a("#select2-drop-mask").hide(),this.dropdown.removeAttr("id"),this.dropdown.hide(),this.container.removeClass("select2-dropdown-open"),this.results.empty(),this.clearSearch(),this.search.removeClass("select2-active"),this.opts.element.trigger(a.Event("select2-close"))}},externalSearch:function(a){this.open(),this.search.val(a),this.updateResults(!1)},clearSearch:function(){},getMaximumSelectionSize:function(){return I(this.opts.maximumSelectionSize)},ensureHighlightVisible:function(){var d,e,f,g,h,i,j,c=this.results;if(e=this.highlight(),!(0>e)){if(0==e)return c.scrollTop(0),b;d=this.findHighlightableChoices().find(".select2-result-label"),f=a(d[e]),g=f.offset().top+f.outerHeight(!0),e===d.length-1&&(j=c.find("li.select2-more-results"),j.length>0&&(g=j.offset().top+j.outerHeight(!0))),h=c.offset().top+c.outerHeight(!0),g>h&&c.scrollTop(c.scrollTop()+(g-h)),i=f.offset().top-c.offset().top,0>i&&"none"!=f.css("display")&&c.scrollTop(c.scrollTop()+i)}},findHighlightableChoices:function(){return this.results.find(".select2-result-selectable:not(.select2-selected):not(.select2-disabled)")},moveHighlight:function(b){for(var c=this.findHighlightableChoices(),d=this.highlight();d>-1&&c.length>d;){d+=b;var e=a(c[d]);if(e.hasClass("select2-result-selectable")&&!e.hasClass("select2-disabled")&&!e.hasClass("select2-selected")){this.highlight(d);break}}},highlight:function(c){var e,f,d=this.findHighlightableChoices();return 0===arguments.length?m(d.filter(".select2-highlighted")[0],d.get()):(c>=d.length&&(c=d.length-1),0>c&&(c=0),this.results.find(".select2-highlighted").removeClass("select2-highlighted"),e=a(d[c]),e.addClass("select2-highlighted"),this.ensureHighlightVisible(),f=e.data("select2-data"),f&&this.opts.element.trigger({type:"select2-highlight",val:this.id(f),choice:f}),b)},countSelectableResults:function(){return this.findHighlightableChoices().length},highlightUnderEvent:function(b){var c=a(b.target).closest(".select2-result-selectable");if(c.length>0&&!c.is(".select2-highlighted")){var d=this.findHighlightableChoices();this.highlight(d.index(c))}else 0==c.length&&this.results.find(".select2-highlighted").removeClass("select2-highlighted")},loadMoreIfNeeded:function(){var c,a=this.results,b=a.find("li.select2-more-results"),e=this.resultsPage+1,f=this,g=this.search.val(),h=this.context;0!==b.length&&(c=b.offset().top-a.offset().top-a.height(),this.opts.loadMorePadding>=c&&(b.addClass("select2-active"),this.opts.query({element:this.opts.element,term:g,page:e,context:h,matcher:this.opts.matcher,callback:this.bind(function(c){f.opened()&&(f.opts.populateResults.call(this,a,c.results,{term:g,page:e,context:h}),f.postprocessResults(c,!1,!1),c.more===!0?(b.detach().appendTo(a).text(f.opts.formatLoadMore(e+1)),window.setTimeout(function(){f.loadMoreIfNeeded()},10)):b.remove(),f.positionDropdown(),f.resultsPage=e,f.context=c.context)})})))},tokenize:function(){},updateResults:function(c){function l(){d.removeClass("select2-active"),h.positionDropdown()}function m(a){e.html(a),l()}var g,i,d=this.search,e=this.results,f=this.opts,h=this,j=d.val(),k=a.data(this.container,"select2-last-term");if((c===!0||!k||!o(j,k))&&(a.data(this.container,"select2-last-term",j),c===!0||this.showSearchInput!==!1&&this.opened())){var n=this.getMaximumSelectionSize();if(n>=1&&(g=this.data(),a.isArray(g)&&g.length>=n&&H(f.formatSelectionTooBig,"formatSelectionTooBig")))return m("<li class='select2-selection-limit'>"+f.formatSelectionTooBig(n)+"</li>"),b;if(d.val().length<f.minimumInputLength)return H(f.formatInputTooShort,"formatInputTooShort")?m("<li class='select2-no-results'>"+f.formatInputTooShort(d.val(),f.minimumInputLength)+"</li>"):m(""),c&&this.showSearch&&this.showSearch(!0),b;if(f.maximumInputLength&&d.val().length>f.maximumInputLength)return H(f.formatInputTooLong,"formatInputTooLong")?m("<li class='select2-no-results'>"+f.formatInputTooLong(d.val(),f.maximumInputLength)+"</li>"):m(""),b;f.formatSearching&&0===this.findHighlightableChoices().length&&m("<li class='select2-searching'>"+f.formatSearching()+"</li>"),d.addClass("select2-active"),i=this.tokenize(),i!=b&&null!=i&&d.val(i),this.resultsPage=1,f.query({element:f.element,term:d.val(),page:this.resultsPage,context:null,matcher:f.matcher,callback:this.bind(function(g){var i;return this.opened()?(this.context=g.context===b?null:g.context,this.opts.createSearchChoice&&""!==d.val()&&(i=this.opts.createSearchChoice.call(h,d.val(),g.results),i!==b&&null!==i&&h.id(i)!==b&&null!==h.id(i)&&0===a(g.results).filter(function(){return o(h.id(this),h.id(i))}).length&&g.results.unshift(i)),0===g.results.length&&H(f.formatNoMatches,"formatNoMatches")?(m("<li class='select2-no-results'>"+f.formatNoMatches(d.val())+"</li>"),b):(e.empty(),h.opts.populateResults.call(this,e,g.results,{term:d.val(),page:this.resultsPage,context:null}),g.more===!0&&H(f.formatLoadMore,"formatLoadMore")&&(e.append("<li class='select2-more-results'>"+h.opts.escapeMarkup(f.formatLoadMore(this.resultsPage))+"</li>"),window.setTimeout(function(){h.loadMoreIfNeeded()},10)),this.postprocessResults(g,c),l(),this.opts.element.trigger({type:"select2-loaded",items:g}),b)):(this.search.removeClass("select2-active"),b)})})}},cancel:function(){this.close()},blur:function(){this.opts.selectOnBlur&&this.selectHighlighted({noFocus:!0}),this.close(),this.container.removeClass("select2-container-active"),this.search[0]===document.activeElement&&this.search.blur(),this.clearSearch(),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus")},focusSearch:function(){w(this.search)},selectHighlighted:function(a){var b=this.highlight(),c=this.results.find(".select2-highlighted"),d=c.closest(".select2-result").data("select2-data");d?(this.highlight(b),this.onSelect(d,a)):a&&a.noFocus&&this.close()},getPlaceholder:function(){var a;return this.opts.element.attr("placeholder")||this.opts.element.attr("data-placeholder")||this.opts.element.data("placeholder")||this.opts.placeholder||((a=this.getPlaceholderOption())!==b?a.text():b)},getPlaceholderOption:function(){if(this.select){var a=this.select.children().first();if(this.opts.placeholderOption!==b)return"first"===this.opts.placeholderOption&&a||"function"==typeof this.opts.placeholderOption&&this.opts.placeholderOption(this.select);if(""===a.text()&&""===a.val())return a}},initContainerWidth:function(){function c(){var c,d,e,f,g;if("off"===this.opts.width)return null;if("element"===this.opts.width)return 0===this.opts.element.outerWidth(!1)?"auto":this.opts.element.outerWidth(!1)+"px";if("copy"===this.opts.width||"resolve"===this.opts.width){if(c=this.opts.element.attr("style"),c!==b)for(d=c.split(";"),f=0,g=d.length;g>f;f+=1)if(e=d[f].replace(/\s/g,"").match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i),null!==e&&e.length>=1)return e[1];return"resolve"===this.opts.width?(c=this.opts.element.css("width"),c.indexOf("%")>0?c:0===this.opts.element.outerWidth(!1)?"auto":this.opts.element.outerWidth(!1)+"px"):null}return a.isFunction(this.opts.width)?this.opts.width():this.opts.width}var d=c.call(this);null!==d&&this.container.css("width",d)}}),e=L(d,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container"}).html(["<a href='javascript:void(0)' onclick='return false;' class='select2-choice' tabindex='-1'>"," <span class='select2-chosen'> </span><abbr class='select2-search-choice-close'></abbr>"," <span class='select2-arrow'><b></b></span>","</a>","<input class='select2-focusser select2-offscreen' type='text'/>","<div class='select2-drop select2-display-none'>"," <div class='select2-search'>"," <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'/>"," </div>"," <ul class='select2-results'>"," </ul>","</div>"].join(""));return b},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.focusser.prop("disabled",!this.isInterfaceEnabled())},opening:function(){var b,c,d;this.opts.minimumResultsForSearch>=0&&this.showSearch(!0),this.parent.opening.apply(this,arguments),this.showSearchInput!==!1&&this.search.val(this.focusser.val()),this.search.focus(),b=this.search.get(0),b.createTextRange?(c=b.createTextRange(),c.collapse(!1),c.select()):b.setSelectionRange&&(d=this.search.val().length,b.setSelectionRange(d,d)),this.focusser.prop("disabled",!0).val(""),this.updateResults(!0),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.focusser.removeAttr("disabled"),this.focusser.focus())},focus:function(){this.opened()?this.close():(this.focusser.removeAttr("disabled"),this.focusser.focus())},isFocused:function(){return this.container.hasClass("select2-container-active")},cancel:function(){this.parent.cancel.apply(this,arguments),this.focusser.removeAttr("disabled"),this.focusser.focus()},initContainer:function(){var d,e=this.container,f=this.dropdown;0>this.opts.minimumResultsForSearch?this.showSearch(!1):this.showSearch(!0),this.selection=d=e.find(".select2-choice"),this.focusser=e.find(".select2-focusser"),this.focusser.attr("id","s2id_autogen"+g()),a("label[for='"+this.opts.element.attr("id")+"']").attr("for",this.focusser.attr("id")),this.focusser.attr("tabindex",this.elementTabIndex),this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){if(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)return y(a),b;switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),y(a),b;case c.ENTER:return this.selectHighlighted(),y(a),b;case c.TAB:return this.selectHighlighted({noFocus:!0}),b;case c.ESC:return this.cancel(a),y(a),b}}})),this.search.on("blur",this.bind(function(){document.activeElement===this.body().get(0)&&window.setTimeout(this.bind(function(){this.search.focus()}),0)})),this.focusser.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.ESC){if(this.opts.openOnEnter===!1&&a.which===c.ENTER)return y(a),b;if(a.which==c.DOWN||a.which==c.UP||a.which==c.ENTER&&this.opts.openOnEnter){if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return;return this.open(),y(a),b}return a.which==c.DELETE||a.which==c.BACKSPACE?(this.opts.allowClear&&this.clear(),y(a),b):b}})),r(this.focusser),this.focusser.on("keyup-change input",this.bind(function(a){if(this.opts.minimumResultsForSearch>=0){if(a.stopPropagation(),this.opened())return;this.open()}})),d.on("mousedown","abbr",this.bind(function(a){this.isInterfaceEnabled()&&(this.clear(),z(a),this.close(),this.selection.focus())})),d.on("mousedown",this.bind(function(b){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.opened()?this.close():this.isInterfaceEnabled()&&this.open(),y(b)})),f.on("mousedown",this.bind(function(){this.search.focus()})),d.on("focus",this.bind(function(a){y(a)})),this.focusser.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})).on("blur",this.bind(function(){this.opened()||(this.container.removeClass("select2-container-active"),this.opts.element.trigger(a.Event("select2-blur")))})),this.search.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.setPlaceholder()},clear:function(a){var b=this.selection.data("select2-data");if(b){var c=this.getPlaceholderOption();this.opts.element.val(c?c.val():""),this.selection.find(".select2-chosen").empty(),this.selection.removeData("select2-data"),this.setPlaceholder(),a!==!1&&(this.opts.element.trigger({type:"select2-removed",val:this.id(b),choice:b}),this.triggerChange({removed:b}))}},initSelection:function(){if(this.isPlaceholderOptionSelected())this.updateSelection([]),this.close(),this.setPlaceholder();else{var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.setPlaceholder())})}},isPlaceholderOptionSelected:function(){var a;return(a=this.getPlaceholderOption())!==b&&a.is(":selected")||""===this.opts.element.val()||this.opts.element.val()===b||null===this.opts.element.val()},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=a.find(":selected");b(c.optionToData(d))}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=c.val(),f=null;b.query({matcher:function(a,c,d){var g=o(e,b.id(d));return g&&(f=d),g},callback:a.isFunction(d)?function(){d(f)}:a.noop})}),b},getPlaceholder:function(){return this.select&&this.getPlaceholderOption()===b?b:this.parent.getPlaceholder.apply(this,arguments)},setPlaceholder:function(){var a=this.getPlaceholder();if(this.isPlaceholderOptionSelected()&&a!==b){if(this.select&&this.getPlaceholderOption()===b)return;this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.container.removeClass("select2-allowclear")}},postprocessResults:function(a,c,d){var e=0,f=this;if(this.findHighlightableChoices().each2(function(a,c){return o(f.id(c.data("select2-data")),f.opts.element.val())?(e=a,!1):b}),d!==!1&&(c===!0&&e>=0?this.highlight(e):this.highlight(0)),c===!0){var h=this.opts.minimumResultsForSearch;h>=0&&this.showSearch(J(a.results)>=h)}},showSearch:function(b){this.showSearchInput!==b&&(this.showSearchInput=b,this.dropdown.find(".select2-search").toggleClass("select2-search-hidden",!b),this.dropdown.find(".select2-search").toggleClass("select2-offscreen",!b),a(this.dropdown,this.container).toggleClass("select2-with-searchbox",b))},onSelect:function(a,b){if(this.triggerSelect(a)){var c=this.opts.element.val(),d=this.data();this.opts.element.val(this.id(a)),this.updateSelection(a),this.opts.element.trigger({type:"select2-selected",val:this.id(a),choice:a}),this.close(),b&&b.noFocus||this.selection.focus(),o(c,this.id(a))||this.triggerChange({added:a,removed:d})}},updateSelection:function(a){var d,e,c=this.selection.find(".select2-chosen");this.selection.data("select2-data",a),c.empty(),d=this.opts.formatSelection(a,c,this.opts.escapeMarkup),d!==b&&c.append(d),e=this.opts.formatSelectionCssClass(a,c),e!==b&&c.addClass(e),this.selection.removeClass("select2-default"),this.opts.allowClear&&this.getPlaceholder()!==b&&this.container.addClass("select2-allowclear") +},val:function(){var a,c=!1,d=null,e=this,f=this.data();if(0===arguments.length)return this.opts.element.val();if(a=arguments[0],arguments.length>1&&(c=arguments[1]),this.select)this.select.val(a).find(":selected").each2(function(a,b){return d=e.optionToData(b),!1}),this.updateSelection(d),this.setPlaceholder(),c&&this.triggerChange({added:d,removed:f});else{if(!a&&0!==a)return this.clear(c),b;if(this.opts.initSelection===b)throw Error("cannot call val() if initSelection() is not defined");this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){e.opts.element.val(a?e.id(a):""),e.updateSelection(a),e.setPlaceholder(),c&&e.triggerChange({added:a,removed:f})})}},clearSearch:function(){this.search.val(""),this.focusser.val("")},data:function(a,c){var d;return 0===arguments.length?(d=this.selection.data("select2-data"),d==b&&(d=null),d):(a&&""!==a?(d=this.data(),this.opts.element.val(a?this.id(a):""),this.updateSelection(a),c&&this.triggerChange({added:a,removed:d})):this.clear(c),b)}}),f=L(d,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container select2-container-multi"}).html(["<ul class='select2-choices'>"," <li class='select2-search-field'>"," <input type='text' autocomplete='off' autocorrect='off' autocapitilize='off' spellcheck='false' class='select2-input'>"," </li>","</ul>","<div class='select2-drop select2-drop-multi select2-display-none'>"," <ul class='select2-results'>"," </ul>","</div>"].join(""));return b},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=[];a.find(":selected").each2(function(a,b){d.push(c.optionToData(b))}),b(d)}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=p(c.val(),b.separator),f=[];b.query({matcher:function(c,d,g){var h=a.grep(e,function(a){return o(a,b.id(g))}).length;return h&&f.push(g),h},callback:a.isFunction(d)?function(){for(var a=[],c=0;e.length>c;c++)for(var g=e[c],h=0;f.length>h;h++){var i=f[h];if(o(g,b.id(i))){a.push(i),f.splice(h,1);break}}d(a)}:a.noop})}),b},selectChoice:function(a){var b=this.container.find(".select2-search-choice-focus");b.length&&a&&a[0]==b[0]||(b.length&&this.opts.element.trigger("choice-deselected",b),b.removeClass("select2-search-choice-focus"),a&&a.length&&(this.close(),a.addClass("select2-search-choice-focus"),this.opts.element.trigger("choice-selected",a)))},initContainer:function(){var e,d=".select2-choices";this.searchContainer=this.container.find(".select2-search-field"),this.selection=e=this.container.find(d);var f=this;this.selection.on("mousedown",".select2-search-choice",function(){f.search[0].focus(),f.selectChoice(a(this))}),this.search.attr("id","s2id_autogen"+g()),a("label[for='"+this.opts.element.attr("id")+"']").attr("for",this.search.attr("id")),this.search.on("input paste",this.bind(function(){this.isInterfaceEnabled()&&(this.opened()||this.open())})),this.search.attr("tabindex",this.elementTabIndex),this.keydowns=0,this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){++this.keydowns;var d=e.find(".select2-search-choice-focus"),f=d.prev(".select2-search-choice:not(.select2-locked)"),g=d.next(".select2-search-choice:not(.select2-locked)"),h=x(this.search);if(d.length&&(a.which==c.LEFT||a.which==c.RIGHT||a.which==c.BACKSPACE||a.which==c.DELETE||a.which==c.ENTER)){var i=d;return a.which==c.LEFT&&f.length?i=f:a.which==c.RIGHT?i=g.length?g:null:a.which===c.BACKSPACE?(this.unselect(d.first()),this.search.width(10),i=f.length?f:g):a.which==c.DELETE?(this.unselect(d.first()),this.search.width(10),i=g.length?g:null):a.which==c.ENTER&&(i=null),this.selectChoice(i),y(a),i&&i.length||this.open(),b}if((a.which===c.BACKSPACE&&1==this.keydowns||a.which==c.LEFT)&&0==h.offset&&!h.length)return this.selectChoice(e.find(".select2-search-choice:not(.select2-locked)").last()),y(a),b;if(this.selectChoice(null),this.opened())switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),y(a),b;case c.ENTER:return this.selectHighlighted(),y(a),b;case c.TAB:return this.selectHighlighted({noFocus:!0}),this.close(),b;case c.ESC:return this.cancel(a),y(a),b}if(a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.BACKSPACE&&a.which!==c.ESC){if(a.which===c.ENTER){if(this.opts.openOnEnter===!1)return;if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return}this.open(),(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)&&y(a),a.which===c.ENTER&&y(a)}}})),this.search.on("keyup",this.bind(function(){this.keydowns=0,this.resizeSearch()})),this.search.on("blur",this.bind(function(b){this.container.removeClass("select2-container-active"),this.search.removeClass("select2-focused"),this.selectChoice(null),this.opened()||this.clearSearch(),b.stopImmediatePropagation(),this.opts.element.trigger(a.Event("select2-blur"))})),this.container.on("click",d,this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").length>0||(this.selectChoice(null),this.clearPlaceholder(),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.open(),this.focusSearch(),b.preventDefault()))})),this.container.on("focus",d,this.bind(function(){this.isInterfaceEnabled()&&(this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.clearSearch()},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.search.prop("disabled",!this.isInterfaceEnabled())},initSelection:function(){if(""===this.opts.element.val()&&""===this.opts.element.text()&&(this.updateSelection([]),this.close(),this.clearSearch()),this.select||""!==this.opts.element.val()){var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.clearSearch())})}},clearSearch:function(){var a=this.getPlaceholder(),c=this.getMaxSearchWidth();a!==b&&0===this.getVal().length&&this.search.hasClass("select2-focused")===!1?(this.search.val(a).addClass("select2-default"),this.search.width(c>0?c:this.container.css("width"))):this.search.val("").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")&&this.search.val("").removeClass("select2-default")},opening:function(){this.clearPlaceholder(),this.resizeSearch(),this.parent.opening.apply(this,arguments),this.focusSearch(),this.updateResults(!0),this.search.focus(),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&this.parent.close.apply(this,arguments)},focus:function(){this.close(),this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(b){var c=[],d=[],e=this;a(b).each(function(){0>m(e.id(this),c)&&(c.push(e.id(this)),d.push(this))}),b=d,this.selection.find(".select2-search-choice").remove(),a(b).each(function(){e.addSelectedChoice(this)}),e.postprocessResults()},tokenize:function(){var a=this.search.val();a=this.opts.tokenizer.call(this,a,this.data(),this.bind(this.onSelect),this.opts),null!=a&&a!=b&&(this.search.val(a),a.length>0&&this.open())},onSelect:function(a,b){this.triggerSelect(a)&&(this.addSelectedChoice(a),this.opts.element.trigger({type:"selected",val:this.id(a),choice:a}),(this.select||!this.opts.closeOnSelect)&&this.postprocessResults(),this.opts.closeOnSelect?(this.close(),this.search.width(10)):this.countSelectableResults()>0?(this.search.width(10),this.resizeSearch(),this.getMaximumSelectionSize()>0&&this.val().length>=this.getMaximumSelectionSize()&&this.updateResults(!0),this.positionDropdown()):(this.close(),this.search.width(10)),this.triggerChange({added:a}),b&&b.noFocus||this.focusSearch())},cancel:function(){this.close(),this.focusSearch()},addSelectedChoice:function(c){var j,k,d=!c.locked,e=a("<li class='select2-search-choice'> <div></div> <a href='#' onclick='return false;' class='select2-search-choice-close' tabindex='-1'></a></li>"),f=a("<li class='select2-search-choice select2-locked'><div></div></li>"),g=d?e:f,h=this.id(c),i=this.getVal();j=this.opts.formatSelection(c,g.find("div"),this.opts.escapeMarkup),j!=b&&g.find("div").replaceWith("<div>"+j+"</div>"),k=this.opts.formatSelectionCssClass(c,g.find("div")),k!=b&&g.addClass(k),d&&g.find(".select2-search-choice-close").on("mousedown",y).on("click dblclick",this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").fadeOut("fast",this.bind(function(){this.unselect(a(b.target)),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"),this.close(),this.focusSearch()})).dequeue(),y(b))})).on("focus",this.bind(function(){this.isInterfaceEnabled()&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))})),g.data("select2-data",c),g.insertBefore(this.searchContainer),i.push(h),this.setVal(i)},unselect:function(a){var c,d,b=this.getVal();if(a=a.closest(".select2-search-choice"),0===a.length)throw"Invalid argument: "+a+". Must be .select2-search-choice";c=a.data("select2-data"),c&&(d=m(this.id(c),b),d>=0&&(b.splice(d,1),this.setVal(b),this.select&&this.postprocessResults()),a.remove(),this.opts.element.trigger({type:"removed",val:this.id(c),choice:c}),this.triggerChange({removed:c}))},postprocessResults:function(a,b,c){var d=this.getVal(),e=this.results.find(".select2-result"),f=this.results.find(".select2-result-with-children"),g=this;e.each2(function(a,b){var c=g.id(b.data("select2-data"));m(c,d)>=0&&(b.addClass("select2-selected"),b.find(".select2-result-selectable").addClass("select2-selected"))}),f.each2(function(a,b){b.is(".select2-result-selectable")||0!==b.find(".select2-result-selectable:not(.select2-selected)").length||b.addClass("select2-selected")}),-1==this.highlight()&&c!==!1&&g.highlight(0),!this.opts.createSearchChoice&&!e.filter(".select2-result:not(.select2-selected)").length>0&&(!a||a&&!a.more&&0===this.results.find(".select2-no-results").length)&&H(g.opts.formatNoMatches,"formatNoMatches")&&this.results.append("<li class='select2-no-results'>"+g.opts.formatNoMatches(g.search.val())+"</li>")},getMaxSearchWidth:function(){return this.selection.width()-q(this.search)},resizeSearch:function(){var a,b,c,d,e,f=q(this.search);a=A(this.search)+10,b=this.search.offset().left,c=this.selection.width(),d=this.selection.offset().left,e=c-(b-d)-f,a>e&&(e=c-f),40>e&&(e=c-f),0>=e&&(e=a),this.search.width(e)},getVal:function(){var a;return this.select?(a=this.select.val(),null===a?[]:a):(a=this.opts.element.val(),p(a,this.opts.separator))},setVal:function(b){var c;this.select?this.select.val(b):(c=[],a(b).each(function(){0>m(this,c)&&c.push(this)}),this.opts.element.val(0===c.length?"":c.join(this.opts.separator)))},buildChangeDetails:function(a,b){for(var b=b.slice(0),a=a.slice(0),c=0;b.length>c;c++)for(var d=0;a.length>d;d++)o(this.opts.id(b[c]),this.opts.id(a[d]))&&(b.splice(c,1),c--,a.splice(d,1),d--);return{added:b,removed:a}},val:function(c,d){var e,f=this;if(0===arguments.length)return this.getVal();if(e=this.data(),e.length||(e=[]),!c&&0!==c)return this.opts.element.val(""),this.updateSelection([]),this.clearSearch(),d&&this.triggerChange({added:this.data(),removed:e}),b;if(this.setVal(c),this.select)this.opts.initSelection(this.select,this.bind(this.updateSelection)),d&&this.triggerChange(this.buildChangeDetails(e,this.data()));else{if(this.opts.initSelection===b)throw Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,function(b){var c=a.map(b,f.id);f.setVal(c),f.updateSelection(b),f.clearSearch(),d&&f.triggerChange(this.buildChangeDetails(e,this.data()))})}this.clearSearch()},onSortStart:function(){if(this.select)throw Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");this.search.width(0),this.searchContainer.hide()},onSortEnd:function(){var b=[],c=this;this.searchContainer.show(),this.searchContainer.appendTo(this.searchContainer.parent()),this.resizeSearch(),this.selection.find(".select2-search-choice").each(function(){b.push(c.opts.id(a(this).data("select2-data")))}),this.setVal(b),this.triggerChange()},data:function(c,d){var f,g,e=this;return 0===arguments.length?this.selection.find(".select2-search-choice").map(function(){return a(this).data("select2-data")}).get():(g=this.data(),c||(c=[]),f=a.map(c,function(a){return e.opts.id(a)}),this.setVal(f),this.updateSelection(c),this.clearSearch(),d&&this.triggerChange(this.buildChangeDetails(g,this.data())),b)}}),a.fn.select2=function(){var d,g,h,i,j,c=Array.prototype.slice.call(arguments,0),k=["val","destroy","opened","open","close","focus","isFocused","container","dropdown","onSortStart","onSortEnd","enable","readonly","positionDropdown","data","search"],l=["val","opened","isFocused","container","data"],n={search:"externalSearch"};return this.each(function(){if(0===c.length||"object"==typeof c[0])d=0===c.length?{}:a.extend({},c[0]),d.element=a(this),"select"===d.element.get(0).tagName.toLowerCase()?j=d.element.prop("multiple"):(j=d.multiple||!1,"tags"in d&&(d.multiple=j=!0)),g=j?new f:new e,g.init(d);else{if("string"!=typeof c[0])throw"Invalid arguments to select2 plugin: "+c;if(0>m(c[0],k))throw"Unknown method: "+c[0];if(i=b,g=a(this).data("select2"),g===b)return;if(h=c[0],"container"===h?i=g.container:"dropdown"===h?i=g.dropdown:(n[h]&&(h=n[h]),i=g[h].apply(g,c.slice(1))),m(c[0],l)>=0)return!1}}),i===b?this:i},a.fn.select2.defaults={width:"copy",loadMorePadding:0,closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c,d){var e=[];return C(a.text,c.term,e,d),e.join("")},formatSelection:function(a,c,d){return a?d(a.text):b},sortResults:function(a){return a},formatResultCssClass:function(){return b},formatSelectionCssClass:function(){return b},formatNoMatches:function(){return"No matches found"},formatInputTooShort:function(a,b){var c=b-a.length;return"Please enter "+c+" more character"+(1==c?"":"s")},formatInputTooLong:function(a,b){var c=a.length-b;return"Please delete "+c+" character"+(1==c?"":"s")},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results..."},formatSearching:function(){return"Searching..."},minimumResultsForSearch:0,minimumInputLength:0,maximumInputLength:null,maximumSelectionSize:0,id:function(a){return a.id},matcher:function(a,b){return(""+b).toUpperCase().indexOf((""+a).toUpperCase())>=0},separator:",",tokenSeparators:[],tokenizer:K,escapeMarkup:D,blurOnChange:!1,selectOnBlur:!1,adaptContainerCssClass:function(a){return a},adaptDropdownCssClass:function(){return null}},a.fn.select2.ajaxDefaults={transport:a.ajax,params:{type:"GET",cache:!1,dataType:"json"}},window.Select2={query:{ajax:E,local:F,tags:G},util:{debounce:t,markMatch:C,escapeMarkup:D},"class":{"abstract":d,single:e,multi:f}}}}(jQuery); \ No newline at end of file Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2013-07-18 14:49:49 UTC (rev 369) +++ trunk/pom.xml 2013-07-19 13:50:40 UTC (rev 370) @@ -10,7 +10,7 @@ <parent> <groupId>org.nuiton</groupId> <artifactId>mavenpom4redmine</artifactId> - <version>3.4.7</version> + <version>3.4.11</version> </parent> <groupId>org.chorem</groupId> @@ -211,6 +211,11 @@ </dependency> <dependency> <groupId>org.nuiton.js</groupId> + <artifactId>nuiton-js-angular</artifactId> + <version>1.1.5-1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.nuiton.js</groupId> <artifactId>nuiton-js-jquery</artifactId> <version>1.8.3-1</version> </dependency>
participants (1)
-
bpoussin@users.chorem.org