Chorem-commits
Threads by month
- ----- 2026 -----
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
July 2013
- 3 participants
- 26 discussions
r378 - in trunk/chorem-webmotion/src/main: java/org/chorem/webmotion/actions webapp/WEB-INF/jsp webapp/css webapp/img webapp/js
by meynier@users.chorem.org 29 Jul '13
by meynier@users.chorem.org 29 Jul '13
29 Jul '13
Author: meynier
Date: 2013-07-29 16:51:52 +0200 (Mon, 29 Jul 2013)
New Revision: 378
Url: http://chorem.org/projects/chorem/repository/revisions/378
Log:
Added buttons in employee edit page to edit/validate/cancel all rows \(and various other small improvments\)
Added:
trunk/chorem-webmotion/src/main/webapp/css/employeeEdit.css
trunk/chorem-webmotion/src/main/webapp/img/ajax-loader.gif
Modified:
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-26 14:28:28 UTC (rev 377)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-29 14:51:52 UTC (rev 378)
@@ -128,9 +128,14 @@
}
public Render requestAdc(ChoremClient client, String employeeId) {
- EmployeeHR e = new EmployeeHRImpl(client.restore(employeeId));
+ Wikitty w = client.restore(employeeId);
+ EmployeeHR e = new EmployeeHRImpl(w);
AdcCalculation adcCalc = new AdcCalculation(e, client);
double adc = adcCalc.getAdc();
+
+ w.setField("EmployeeHR", "dailyReturn", adc);
+ client.store(w);
+
int iadc = (int)(adc*100);
return renderJSON("adc", (double)(iadc/100.0));
@@ -262,6 +267,10 @@
int x = (int)(client.getDailyHoursWorked(e)*100);
double y = x/100.0;
this.dailyHoursWorked = y;
+
+ x = (int)(this.dailyReturn*100);
+ y = x/100.0;
+ this.dailyReturn = y;
this.e = e;
}
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-26 14:28:28 UTC (rev 377)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-29 14:51:52 UTC (rev 378)
@@ -112,7 +112,7 @@
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
- <th>Responsable Code Lutin</th>
+ <th>Responsable ${q.object.getSupplier(false).getCompany(false)}</th>
<th>Responsable entreprise</th>
<th>Jours estimés</th>
<th>Jours réels</th>
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-26 14:28:28 UTC (rev 377)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-29 14:51:52 UTC (rev 378)
@@ -28,8 +28,8 @@
<f:setLocale value="${locale}" />
+<link rel="stylesheet" href="<c:url value='/css/employeeEdit.css'/>" />
-
<h1>${title}</h1>
<form method="GET" id="companyForm">
@@ -67,16 +67,30 @@
</c:if>
</form>
-
-
-<table class="table table-striped table-bordered table-condensed">
+<button class="btn btn-success" id="editAllBtn">
+ <i class="icon-edit icon-white"></i>
+ Tout éditer
+ </button>
+
+ <button class="editBtn btn btn-success" id="validateAllBtn" disabled>
+ <i class="icon-ok icon-white"></i>
+ Tout valider
+ </button>
+
+ <button class="editBtn btn btn-danger" id="cancelAllBtn" disabled>
+ <i class="icon-remove icon-white"></i>
+ Tout annuler
+ </button>
+<img class="spinner" style="display:none" src="<c:url value='/img/ajax-loader.gif'/>"/>
+<table class="table table-striped table-bordered table-condensed tableEdit">
<thead>
<th>Employé</th>
<th>Salaire</th>
<th>Heures par jour</th>
<th>Taux de productivité</th>
<th>Temps partiel</th>
- <th>CJM</th>
+ <th>CJM <a class="allCjmRefresh" style="cursor:pointer">
+ <i class="icon icon-refresh"></i></a></th>
<th>Edit</th>
</thead>
<tbody>
@@ -86,7 +100,7 @@
<td class="person"><w:display wikitty="${employee.object.wikitty}"
fqfield="Employee.person" label="" />
</td>
- <td class="salary">
+ <td class="edit salary">
${employee.salary}
</td>
<td class="dailyHoursWorked"><f:formatNumber type="number"
Added: trunk/chorem-webmotion/src/main/webapp/css/employeeEdit.css
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/css/employeeEdit.css (rev 0)
+++ trunk/chorem-webmotion/src/main/webapp/css/employeeEdit.css 2013-07-29 14:51:52 UTC (rev 378)
@@ -0,0 +1,4 @@
+.tableEdit tbody tr td input {
+ width:80%;
+}
+
Added: trunk/chorem-webmotion/src/main/webapp/img/ajax-loader.gif
===================================================================
(Binary files differ)
Property changes on: trunk/chorem-webmotion/src/main/webapp/img/ajax-loader.gif
___________________________________________________________________
Added: svn:mime-type
+ image/gif
Modified: trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-26 14:28:28 UTC (rev 377)
+++ trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-29 14:51:52 UTC (rev 378)
@@ -3,6 +3,8 @@
$(document).ready(function() {
+ var editCounter = 0;
+ var blockCounter = 0;
$("#companySelect").change(function() {
$("#companyForm").submit();
@@ -11,7 +13,23 @@
$(".employeeEdit").click(employeeEdit);
$(".cjmRefresh").click(calculateAdc);
+ $('.allCjmRefresh').click(function() {
+ $('.cjmRefresh').click();
+ });
+ $('#editAllBtn').click(function() {
+ $('.employeeEdit').click();
+ });
+
+ $('#validateAllBtn').click(function() {
+ $('.editOk').click();
+ });
+
+ $('#cancelAllBtn').click(function() {
+ $('.editCancel').click();
+ });
+
+
function employeeEdit() {
var row = $(this).parent().parent();
var salary = row.find(".salary");
@@ -29,6 +47,11 @@
cjm.html("<input type = 'text' size='3' value='"+$.trim(cjm.find('.adc').html()).replace('%','')+"'/>");
displayConfirmation(row);
+ editCounter++;
+ if(editCounter === 1)
+ $('.editBtn').removeAttr('disabled');
+
+
}
@@ -51,6 +74,10 @@
if(cjm.attr("refresh") === "true")
refresh = true;
displayDailyReturn(row, $.trim(cjm.attr('oldVal')), refresh);
+ editCounter--;
+ if(editCounter === 0)
+ $('.editBtn').attr('disabled', 'disabled');
+
}
@@ -69,6 +96,12 @@
var cjmVal = $.trim(cjm.find("input").attr('value'));
blockEdit(row);
+ blockCounter++;
+ if(blockCounter === 1) {
+ $('.editBtn').attr('disabled', 'disabled');
+ $('#editAllBtn').attr('disabled', 'disabled');
+ $('.spinner').show();
+ }
$.get(createUrl("/admin/employeeEdit/json/editEmployeeValues/",row.attr("id"), "?salaryStr=",
salaryVal, "&productivityRateStr=", prodVal, "&partialTimeStr=", timeVal,
"&dailyReturnStr=", cjmVal),
@@ -85,6 +118,11 @@
"<span class='errorMessage'>"+errors[i]["errorMessage"]+"</span>");
}
displayConfirmation(row);
+ blockCounter--;
+ if(blockCounter === 0) {
+ $('.editBtn').removeAttr('disabled');
+ $('.spinner').hide();
+ }
}
else
{
@@ -96,7 +134,17 @@
displayEdit(row);
if(cell.attr("refresh") !== "true")
cell.attr('refresh', 'true');
-
+ editCounter--;
+ blockCounter--;
+ //Only case where the butotn should be reactivated
+ if(editCounter !== 0 && blockCounter === 0)
+ $('.editBtn').removeAttr('disabled');
+ else
+ $('.editBtn').attr('disabled', 'disabled');
+ if(blockCounter === 0) {
+ $('.spinner').hide();
+ $('#editAllBtn').removeAttr('disabled');
+ }
}
@@ -121,11 +169,11 @@
}
function blockEdit(row) {
- row.find('.cellEdit').html('wait...');
+ row.find('.cellEdit').html('<img src="../img/ajax-loader.gif"/>');
}
function blockDailyReturn(row) {
- row.find('.dailyReturn').html('wait...');
+ row.find('.dailyReturn').html('<img src="../img/ajax-loader.gif"/>');
}
function displayConfirmation(row) {
@@ -146,9 +194,9 @@
function displayDailyReturn(row, val, icon) {
var cell = row.find('.dailyReturn');
- var str = '<span class="adc">' + val + ' </span><a class="cjmRefresh" style="cursor:pointer">'
+ var str = '<span class="adc">' + val + ' </span>'
if(icon)
- str += '<i class="icon icon-refresh"></i></a>'
+ str += '<a class="cjmRefresh" style="cursor:pointer"><i class="icon icon-refresh"></i></a>'
cell.html(str);
row.find(".cjmRefresh").click(calculateAdc);
}
1
0
r377 - in trunk: chorem-entities/src/main/java/org/chorem/project chorem-webmotion/src/main/java/org/chorem/webmotion/actions chorem-webmotion/src/main/webapp/WEB-INF/jsp chorem-webmotion/src/main/webapp/js
by meynier@users.chorem.org 26 Jul '13
by meynier@users.chorem.org 26 Jul '13
26 Jul '13
Author: meynier
Date: 2013-07-26 16:28:28 +0200 (Fri, 26 Jul 2013)
New Revision: 377
Url: http://chorem.org/projects/chorem/repository/revisions/377
Log:
Updated adc calculation method and small changes on the employee edit interface
Modified:
trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
Modified: trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java
===================================================================
--- trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java 2013-07-25 15:23:55 UTC (rev 376)
+++ trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java 2013-07-26 14:28:28 UTC (rev 377)
@@ -3,13 +3,16 @@
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.List;
import org.chorem.ChoremClient;
import org.chorem.ChoremUtil;
+import org.chorem.entities.Employee;
import org.chorem.entities.EmployeeHR;
import org.chorem.entities.Interval;
import org.chorem.entities.Quotation;
import org.chorem.entities.Time;
+import org.chorem.entities.Company;
import org.nuiton.wikitty.query.WikittyQuery;
import org.nuiton.wikitty.query.WikittyQueryMaker;
import org.nuiton.wikitty.query.WikittyQueryResult;
@@ -18,14 +21,20 @@
ChoremClient client;
EmployeeHR e;
+ Company company;
Date start;
Date end;
+ /**
+ * Calculates the total gains made by the company in one year
+ * @return
+ */
public double getTotalGain() {
WikittyQuery gainQuery = new WikittyQueryMaker()
.and()
.exteq("Closed")
+ .containsOne(Quotation.ELEMENT_FIELD_QUOTATION_SUPPLIER, getEmployeesHR())
.or()
.bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, start, end)
.bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, start, end)
@@ -41,27 +50,48 @@
return total;
}
+ /**
+ * calculates all the salaries of the employees ponderated by the productivity rate
+ * @return
+ */
public double getTotalSalaries() {
- //pondéré par pct d eproductivité
- WikittyQuery employeeQuery = new WikittyQueryMaker().exteq("EmployeeHR").end();
- WikittyQueryResult<EmployeeHR> result = client.findAllByQuery(EmployeeHR.class, employeeQuery);
+ //pondéré par pct de productivité
+
double total = 0;
- for(EmployeeHR e : result.getAll()) {
+ for(EmployeeHR e : getEmployeesHR()) {
total += e.getSalary() * (e.getProductivityRate()/100);
}
return total;
}
+ private List<EmployeeHR> getEmployeesHR() {
+ WikittyQuery employeeQuery = new WikittyQueryMaker().and()
+ .exteq("EmployeeHR")
+ .eq(Employee.ELEMENT_FIELD_EMPLOYEE_COMPANY, company)
+ .end();
+ WikittyQueryResult<EmployeeHR> result = client.findAllByQuery(EmployeeHR.class, employeeQuery);
+ return result.getAll();
+
+ }
+
+ /**
+ * Calculates the sum of all the tims object linked to the company
+ * @return
+ */
public double getTotalTimes() {
+ WikittyQuery empQuery = new WikittyQueryMaker()
+ .eq(Employee.ELEMENT_FIELD_EMPLOYEE_COMPANY, company).end();
+ WikittyQueryResult<Employee> empResult = client.findAllByQuery(Employee.class, empQuery);
+
WikittyQuery timeQuery = new WikittyQueryMaker()
.and()
.exteq("Time")
+ .containsOne(Time.ELEMENT_FIELD_TIME_EMPLOYEE, empResult.getAll())
.or()
.bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, start, end)
.bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, start, end)
.end();
-
WikittyQueryResult<Time> result = client.findAllByQuery(Time.class, timeQuery);
double total = 0;
@@ -73,17 +103,18 @@
}
public double getAdc() {
- end = new GregorianCalendar().getTime();
- Calendar start = new GregorianCalendar();
- start.add(Calendar.YEAR, -1);
- this.start = start.getTime();
+
double expenses = getTotalGain();
double salaries = getTotalSalaries();
double times = getTotalTimes();
double dailyHoursWorked = client.getDailyHoursWorked(e);
double dailySalary = client.restore(e.getWikittyId()).getFieldAsDouble("EmployeeHR", "salary")/21.5;
-
+ System.out.println("GAIN : " + expenses);
+ System.out.println("SALAIRES : " + salaries);
+ System.out.println("TIMES : " + times);
+ System.out.println("DAILY HW : " + dailyHoursWorked);
+ System.out.println("DAILY SALARY : " + dailySalary);
double adc = ( (expenses/salaries)/times ) * dailyHoursWorked * dailySalary;
@@ -93,7 +124,12 @@
public AdcCalculation(EmployeeHR e, ChoremClient client) {
this.e = e;
+ this.company = e.getCompany(false);
this.client = client;
+ this.end = new GregorianCalendar().getTime();
+ Calendar start = new GregorianCalendar();
+ start.add(Calendar.YEAR, -1);
+ this.start = start.getTime();
}
Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-25 15:23:55 UTC (rev 376)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-26 14:28:28 UTC (rev 377)
@@ -130,10 +130,9 @@
public Render requestAdc(ChoremClient client, String employeeId) {
EmployeeHR e = new EmployeeHRImpl(client.restore(employeeId));
AdcCalculation adcCalc = new AdcCalculation(e, client);
- System.out.println("Adc created");
double adc = adcCalc.getAdc();
- System.out.println("ADC calculated");
- return renderJSON("adc", adc);
+ int iadc = (int)(adc*100);
+ return renderJSON("adc", (double)(iadc/100.0));
}
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-25 15:23:55 UTC (rev 376)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-26 14:28:28 UTC (rev 377)
@@ -95,7 +95,11 @@
maxFractionDigits="2" value="${employee.productivityRate}"/>%</td>
<td class="partialTime"><f:formatNumber type="number"
maxFractionDigits="2" value="${employee.partialTime}"/>%</td>
- <td class="dailyReturn"><span class="adc"><f:formatNumber type="number"
+ <td class="dailyReturn"
+ <c:if test='${employee.object.wikitty.hasExtension("EmployeeHR")}'>
+ refresh="true"
+ </c:if>
+ ><span class="adc"><f:formatNumber type="number"
maxFractionDigits="2" value="${employee.dailyReturn}"/></span>
<c:if test='${employee.object.wikitty.hasExtension("EmployeeHR")}'>
<a class="cjmRefresh" style="cursor:pointer">
Modified: trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-25 15:23:55 UTC (rev 376)
+++ trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-26 14:28:28 UTC (rev 377)
@@ -47,7 +47,10 @@
displayEdit(row);
- displayDailyReturn(row, $.trim(cjm.attr('oldVal')));
+ var refresh = false;
+ if(cjm.attr("refresh") === "true")
+ refresh = true;
+ displayDailyReturn(row, $.trim(cjm.attr('oldVal')), refresh);
}
@@ -88,9 +91,11 @@
salary.html(data['salary']);
prod.html(data["productivityRate"]+"%");
time.html(data['partialTime']+"%");
- displayDailyReturn(row, data['dailyReturn']);
+ displayDailyReturn(row, data['dailyReturn'], true);
row.find('.dailyHoursWorked').html(data['dailyHoursWorked']);
displayEdit(row);
+ if(cell.attr("refresh") !== "true")
+ cell.attr('refresh', 'true');
}
@@ -139,9 +144,12 @@
cell.find(".employeeEdit").click(employeeEdit);
}
- function displayDailyReturn(row, val) {
- row.find('.dailyReturn').html('<span class="adc">' + val + ' </span><a class="cjmRefresh" style="cursor:pointer">'
- +'<i class="icon icon-refresh"></i></a>');
+ function displayDailyReturn(row, val, icon) {
+ var cell = row.find('.dailyReturn');
+ var str = '<span class="adc">' + val + ' </span><a class="cjmRefresh" style="cursor:pointer">'
+ if(icon)
+ str += '<i class="icon icon-refresh"></i></a>'
+ cell.html(str);
row.find(".cjmRefresh").click(calculateAdc);
}
1
0
r376 - in trunk: chorem-entities/src/main/java/org/chorem/project chorem-webmotion/src/main/webapp/WEB-INF/jsp chorem-webmotion/src/main/webapp/js
by meynier@users.chorem.org 25 Jul '13
by meynier@users.chorem.org 25 Jul '13
25 Jul '13
Author: meynier
Date: 2013-07-25 17:23:55 +0200 (Thu, 25 Jul 2013)
New Revision: 376
Url: http://chorem.org/projects/chorem/repository/revisions/376
Log:
Add model and view files
Added:
trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java
Modified:
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
Added: trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java
===================================================================
--- trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java (rev 0)
+++ trunk/chorem-entities/src/main/java/org/chorem/project/AdcCalculation.java 2013-07-25 15:23:55 UTC (rev 376)
@@ -0,0 +1,103 @@
+package org.chorem.project;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.chorem.ChoremClient;
+import org.chorem.ChoremUtil;
+import org.chorem.entities.EmployeeHR;
+import org.chorem.entities.Interval;
+import org.chorem.entities.Quotation;
+import org.chorem.entities.Time;
+import org.nuiton.wikitty.query.WikittyQuery;
+import org.nuiton.wikitty.query.WikittyQueryMaker;
+import org.nuiton.wikitty.query.WikittyQueryResult;
+
+public class AdcCalculation {
+
+ ChoremClient client;
+ EmployeeHR e;
+ Date start;
+ Date end;
+
+ public double getTotalGain() {
+
+ WikittyQuery gainQuery = new WikittyQueryMaker()
+ .and()
+ .exteq("Closed")
+ .or()
+ .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, start, end)
+ .bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, start, end)
+ .end();
+
+ WikittyQueryResult<Quotation> result = client.findAllByQuery(Quotation.class, gainQuery);
+
+ double total = 0;
+ for(Quotation q : result.getAll()) {
+ total += q.getAmount();
+ }
+
+ return total;
+ }
+
+ public double getTotalSalaries() {
+ //pondéré par pct d eproductivité
+ WikittyQuery employeeQuery = new WikittyQueryMaker().exteq("EmployeeHR").end();
+ WikittyQueryResult<EmployeeHR> result = client.findAllByQuery(EmployeeHR.class, employeeQuery);
+
+ double total = 0;
+ for(EmployeeHR e : result.getAll()) {
+ total += e.getSalary() * (e.getProductivityRate()/100);
+ }
+ return total;
+ }
+
+ public double getTotalTimes() {
+ WikittyQuery timeQuery = new WikittyQueryMaker()
+ .and()
+ .exteq("Time")
+ .or()
+ .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, start, end)
+ .bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, start, end)
+ .end();
+
+ WikittyQueryResult<Time> result = client.findAllByQuery(Time.class, timeQuery);
+
+ double total = 0;
+ for(Time t : result.getAll()) {
+ total += ChoremUtil.getPeriodInHours(t.getBeginDate(), t.getEndDate());
+ }
+
+ return total;
+ }
+
+ public double getAdc() {
+ end = new GregorianCalendar().getTime();
+ Calendar start = new GregorianCalendar();
+ start.add(Calendar.YEAR, -1);
+ this.start = start.getTime();
+ double expenses = getTotalGain();
+ double salaries = getTotalSalaries();
+ double times = getTotalTimes();
+
+ double dailyHoursWorked = client.getDailyHoursWorked(e);
+ double dailySalary = client.restore(e.getWikittyId()).getFieldAsDouble("EmployeeHR", "salary")/21.5;
+
+ double adc = ( (expenses/salaries)/times ) * dailyHoursWorked * dailySalary;
+
+
+ return adc;
+ }
+
+
+ public AdcCalculation(EmployeeHR e, ChoremClient client) {
+ this.e = e;
+ this.client = client;
+ }
+
+
+
+
+
+}
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-25 15:22:40 UTC (rev 375)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-25 15:23:55 UTC (rev 376)
@@ -95,18 +95,18 @@
maxFractionDigits="2" value="${employee.productivityRate}"/>%</td>
<td class="partialTime"><f:formatNumber type="number"
maxFractionDigits="2" value="${employee.partialTime}"/>%</td>
- <td class="dailyReturn"><f:formatNumber type="number"
- maxFractionDigits="2" value="${employee.dailyReturn}"/>
+ <td class="dailyReturn"><span class="adc"><f:formatNumber type="number"
+ maxFractionDigits="2" value="${employee.dailyReturn}"/></span>
+ <c:if test='${employee.object.wikitty.hasExtension("EmployeeHR")}'>
<a class="cjmRefresh" style="cursor:pointer">
- <i class="icon icon-refresh"></i></a></td>
- <td class="rowEdit"><a class="employeeEdit" style="cursor:pointer"><i class="icon icon-edit"></i></a></td>
+ <i class="icon icon-refresh"></i></a></c:if></td>
+ <td class="cellEdit"><a class="employeeEdit" style="cursor:pointer"><i class="icon icon-edit"></i></a></td>
</tr>
<c:set var="count" value="${count + 1}"/>
</c:forEach>
</tbody>
</table>
-
<script src="<c:url value='/js/employeeEdit.js'/>"></script>
Modified: trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-25 15:22:40 UTC (rev 375)
+++ trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-25 15:23:55 UTC (rev 376)
@@ -9,7 +9,9 @@
});
$(".employeeEdit").click(employeeEdit);
-
+ $(".cjmRefresh").click(calculateAdc);
+
+
function employeeEdit() {
var row = $(this).parent().parent();
var salary = row.find(".salary");
@@ -23,18 +25,11 @@
prod.html("<input type = 'text' size='3' value='"+$.trim(prod.html()).replace('%','')+"'/>");
time.attr("oldVal", $.trim(time.html()));
time.html("<input type = 'text' size='3' value='"+$.trim(time.html()).replace('%','')+"'/>");
- cjm.attr("oldVal", $.trim(cjm.html()));
- cjm.html("<input type = 'text' size='3' value='"+$.trim(cjm.html()).replace('%','')+"'/>");
+ cjm.attr("oldVal", $.trim(cjm.find('.adc').html()));
+ cjm.html("<input type = 'text' size='3' value='"+$.trim(cjm.find('.adc').html()).replace('%','')+"'/>");
+ displayConfirmation(row);
-
-
- $(this).parent().html("<a class='editOk' style='cursor:pointer'><i class='icon icon-ok'></i></a>"
- + "<a class='editCancel' style='cursor:pointer'><i class='icon icon-remove'></i></a>"
- );
- row.find(".editCancel").click(editCancel);
- row.find(".editOk").click(editOk);
-
}
function editCancel() {
@@ -49,12 +44,10 @@
salary.html($.trim(salary.attr('oldVal')));
prod.html($.trim(prod.attr('oldVal')));
time.html($.trim(time.attr('oldVal')));
- cjm.html($.trim(cjm.attr('oldVal')));
- $(this).parent().html("<a class='employeeEdit' style='cursor:pointer'><i class='icon icon-edit'></i></a>"
- );
- row.find(".employeeEdit").click(employeeEdit);
+ displayEdit(row);
+ displayDailyReturn(row, $.trim(cjm.attr('oldVal')));
}
@@ -71,12 +64,12 @@
var prodVal = $.trim(prod.find("input").attr('value'));
var timeVal = $.trim(time.find("input").attr('value'));
var cjmVal = $.trim(cjm.find("input").attr('value'));
-
+
+ blockEdit(row);
$.get(createUrl("/admin/employeeEdit/json/editEmployeeValues/",row.attr("id"), "?salaryStr=",
salaryVal, "&productivityRateStr=", prodVal, "&partialTimeStr=", timeVal,
"&dailyReturnStr=", cjmVal),
function(ret){
- console.log(ret);
var data = ret['data'];
if(data === "error") {
@@ -88,23 +81,71 @@
row.find("." + errors[i]["field"]).append(
"<span class='errorMessage'>"+errors[i]["errorMessage"]+"</span>");
}
+ displayConfirmation(row);
}
else
{
salary.html(data['salary']);
prod.html(data["productivityRate"]+"%");
time.html(data['partialTime']+"%");
- cjm.html(data['dailyReturn']);
+ displayDailyReturn(row, data['dailyReturn']);
row.find('.dailyHoursWorked').html(data['dailyHoursWorked']);
- cell.html("<a class='employeeEdit' style='cursor:pointer'><i class='icon icon-edit'></i></a>");
+ displayEdit(row);
+
}
- row.find(".employeeEdit").click(employeeEdit);
+
}
);
}
+
+ function calculateAdc() {
+ var row = $(this).parent().parent();
+ var cjm = row.find(".dailyReturn");
+ blockDailyReturn(row);
+ blockEdit(row);
+ $.get(createUrl("/admin/employeeEdit/json/requestAdc/",row.attr("id")),
+ function(ret){
+ var adc = ret['adc'];
+ displayDailyReturn(row, adc);
+ displayEdit(row);
+ });
+
+ }
+
+ function blockEdit(row) {
+ row.find('.cellEdit').html('wait...');
+ }
+
+ function blockDailyReturn(row) {
+ row.find('.dailyReturn').html('wait...');
+ }
+
+ function displayConfirmation(row) {
+ var cell = row.find('.cellEdit');
+ cell.html("<a class='editOk' style='cursor:pointer'><i class='icon icon-ok'></i></a>"
+ + "<a class='editCancel' style='cursor:pointer'><i class='icon icon-remove'></i></a>"
+ );
+ row.find(".editCancel").click(editCancel);
+ row.find(".editOk").click(editOk);
+
+ }
+
+ function displayEdit(row) {
+ var cell = row.find('.cellEdit');
+ cell.html("<a class='employeeEdit' style='cursor:pointer'><i class='icon icon-edit'></i></a>");
+ cell.find(".employeeEdit").click(employeeEdit);
+ }
+
+ function displayDailyReturn(row, val) {
+ row.find('.dailyReturn').html('<span class="adc">' + val + ' </span><a class="cjmRefresh" style="cursor:pointer">'
+ +'<i class="icon icon-refresh"></i></a>');
+ row.find(".cjmRefresh").click(calculateAdc);
+ }
+
+
});
\ No newline at end of file
1
0
r375 - in trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions: . project
by meynier@users.chorem.org 25 Jul '13
by meynier@users.chorem.org 25 Jul '13
25 Jul '13
Author: meynier
Date: 2013-07-25 17:22:40 +0200 (Thu, 25 Jul 2013)
New Revision: 375
Url: http://chorem.org/projects/chorem/repository/revisions/375
Log:
Various modifications on employee edit screen and begined with adc calculation
Modified:
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java
Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-23 14:52:56 UTC (rev 374)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-25 15:22:40 UTC (rev 375)
@@ -1,9 +1,11 @@
package org.chorem.webmotion.actions;
+import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -24,6 +26,8 @@
import org.chorem.entities.Company;
import org.chorem.entities.CompanyImpl;
import org.chorem.entities.Employee;
+import org.chorem.entities.EmployeeHR;
+import org.chorem.entities.EmployeeHRImpl;
import org.chorem.entities.EmployeeImpl;
import org.chorem.entities.Interval;
import org.chorem.entities.Quotation;
@@ -31,6 +35,7 @@
import org.chorem.entities.Task;
import org.chorem.entities.Time;
import org.chorem.entities.Worker;
+import org.chorem.project.AdcCalculation;
import org.chorem.project.QuotationCalculation;
import org.nuiton.wikitty.entities.Wikitty;
import org.nuiton.wikitty.query.WikittyQuery;
@@ -72,6 +77,8 @@
for(Employee e : employeeResult.getAll()) {
employees.add(new EmployeeData(e, client));
}
+
+ Collections.sort(employees);
//simply return them lol
return renderView("employeeEdit.jsp",
"employees",employees,
@@ -120,6 +127,17 @@
return employeeFilter(client, company);
}
+ public Render requestAdc(ChoremClient client, String employeeId) {
+ EmployeeHR e = new EmployeeHRImpl(client.restore(employeeId));
+ AdcCalculation adcCalc = new AdcCalculation(e, client);
+ System.out.println("Adc created");
+ double adc = adcCalc.getAdc();
+ System.out.println("ADC calculated");
+ return renderJSON("adc", adc);
+
+
+ }
+
public Render editEmployeeValues(ChoremClient client, String employeeId, String salaryStr
, String productivityRateStr, String partialTimeStr, String dailyReturnStr) {
Wikitty employeeWikitty = client.restore(employeeId);
@@ -218,7 +236,7 @@
}
- public class EmployeeData {
+ public class EmployeeData implements Comparable{
private Employee e;
private String salary;
@@ -241,7 +259,11 @@
this.partialTime = 100;
}
this.dailyReturn = client.getDailyReturn(e);
- this.dailyHoursWorked = client.getDailyHoursWorked(e);
+ //Simple trick to limit to 2 numbers after digit
+ int x = (int)(client.getDailyHoursWorked(e)*100);
+ double y = x/100.0;
+ this.dailyHoursWorked = y;
+
this.e = e;
}
@@ -267,8 +289,14 @@
return partialTime;
}
+ @Override
+ public int compareTo(Object o) {
+ EmployeeData e = (EmployeeData)o;
+ return this.getObject().getPerson(false).getLastName().compareTo(e.getObject().getPerson(false).getLastName());
+ }
+
}
}
Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java 2013-07-23 14:52:56 UTC (rev 374)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java 2013-07-25 15:22:40 UTC (rev 375)
@@ -106,7 +106,7 @@
extDate.put(me.getValue() + " " + me.getKey(), "/Date(" + date.getTime() +")/");
}
}
-
+
JRender data = new JRender(lTask, extDate, dateStart, dateEnd);
return renderJSON("data", data);
}
1
0
r374 - in trunk/chorem-entities/src/main/java/org/chorem: . project
by meynier@users.chorem.org 23 Jul '13
by meynier@users.chorem.org 23 Jul '13
23 Jul '13
Author: meynier
Date: 2013-07-23 16:52:56 +0200 (Tue, 23 Jul 2013)
New Revision: 374
Url: http://chorem.org/projects/chorem/repository/revisions/374
Log:
Corrected bug on quotation calculation
Modified:
trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java
trunk/chorem-entities/src/main/java/org/chorem/project/QuotationCalculation.java
Modified: trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java
===================================================================
--- trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java 2013-07-23 14:51:55 UTC (rev 373)
+++ trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java 2013-07-23 14:52:56 UTC (rev 374)
@@ -25,6 +25,8 @@
import org.chorem.entities.Attachment;
import org.chorem.entities.ChoremUser;
import org.chorem.entities.Company;
+import org.chorem.entities.CompanyHR;
+import org.chorem.entities.CompanyHRImpl;
import org.chorem.entities.CompanyImpl;
import org.chorem.entities.Configuration;
import org.chorem.entities.ConfigurationImpl;
@@ -392,9 +394,6 @@
* @return
*/
public double getDailyReturn(Employee e) {
- //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- //Code commented before confirmation or modification of the contract implementation
- //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Wikitty w = this.restore(e.getWikittyId());
Wikitty companyW = null;
@@ -402,7 +401,7 @@
companyW = restore(e.getCompany(false).getWikittyId());
if(w.hasExtension("EmployeeHR") && w.getFieldAsDouble("EmployeeHR", "dailyReturn") != 0) {
- return w.getFieldAsDouble("Contract", "dailyReturn");
+ return w.getFieldAsDouble("EmployeeHR", "dailyReturn");
}
else if(companyW != null && companyW.hasExtension("CompanyHR")
&& companyW.getFieldAsDouble("CompanyHR", "dailyReturn") != 0) {
@@ -418,9 +417,7 @@
* @return
*/
public double getDailyHoursWorked(Employee e) {
- //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- //Code commented before confirmation or modification of the contract implementation
- //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
Wikitty w = this.restore(e.getWikittyId());
Wikitty companyW = null;
if(e.getCompany(false) != null)
@@ -440,4 +437,6 @@
return this.getConfiguration().getDailyHoursWorked();
}
}
+
+
}
Modified: trunk/chorem-entities/src/main/java/org/chorem/project/QuotationCalculation.java
===================================================================
--- trunk/chorem-entities/src/main/java/org/chorem/project/QuotationCalculation.java 2013-07-23 14:51:55 UTC (rev 373)
+++ trunk/chorem-entities/src/main/java/org/chorem/project/QuotationCalculation.java 2013-07-23 14:52:56 UTC (rev 374)
@@ -34,7 +34,7 @@
public HashMap<Employee, Double> getPercentages() {
HashMap<Employee, Double> percentages = new HashMap<Employee, Double>();
double totalDays = 0;
- for(Task t : tasks)
+ for(Task t : getTasks())
totalDays += t.getEstimatedDays();
List<Task> tasks = getTasks();
1
0
r373 - in trunk/chorem-webmotion/src/main: java/org/chorem/webmotion/actions resources webapp/WEB-INF/jsp webapp/js
by meynier@users.chorem.org 23 Jul '13
by meynier@users.chorem.org 23 Jul '13
23 Jul '13
Author: meynier
Date: 2013-07-23 16:51:55 +0200 (Tue, 23 Jul 2013)
New Revision: 373
Url: http://chorem.org/projects/chorem/repository/revisions/373
Log:
Added a page that displays all the employees of a company and allows to edit the informations. It will also allow to calculate the average daily cost.
Added:
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
Modified:
trunk/chorem-webmotion/src/main/resources/mapping
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp
Added: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java (rev 0)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/EmployeeEditAction.java 2013-07-23 14:51:55 UTC (rev 373)
@@ -0,0 +1,274 @@
+package org.chorem.webmotion.actions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.debux.webmotion.server.WebMotionController;
+import org.chorem.ChoremClient;
+import org.chorem.ChoremUtil;
+import org.debux.webmotion.server.call.Call;
+import org.debux.webmotion.server.render.Render;
+import org.chorem.entities.Closed;
+import org.chorem.entities.Company;
+import org.chorem.entities.CompanyImpl;
+import org.chorem.entities.Employee;
+import org.chorem.entities.EmployeeImpl;
+import org.chorem.entities.Interval;
+import org.chorem.entities.Quotation;
+import org.chorem.entities.Project;
+import org.chorem.entities.Task;
+import org.chorem.entities.Time;
+import org.chorem.entities.Worker;
+import org.chorem.project.QuotationCalculation;
+import org.nuiton.wikitty.entities.Wikitty;
+import org.nuiton.wikitty.query.WikittyQuery;
+import org.nuiton.wikitty.query.WikittyQueryMaker;
+import org.nuiton.wikitty.query.WikittyQueryResult;
+
+/**
+ *
+ * @author meynier
+ *
+ */
+public class EmployeeEditAction extends WebMotionController {
+
+ /** to use log facility, just put in your code: log.info(\"...\"); */
+ static private Log log = LogFactory.getLog(EmployeeEditAction.class);
+
+ /**
+ * Return the view for a single quotation
+ * @param client Chorem client
+ * @param id Project ID (useless here)
+ * @param quotationFilter Quotation id
+ * @return
+ */
+ public Render employeeFilter(ChoremClient client, Company company) {
+ //get all the empoyees from the default company
+
+
+
+
+ List<Company> companies = client.findAllByQuery(Company.class,
+ new WikittyQueryMaker().exteq("Company").end()).getAll();
+
+ //Order by name
+ WikittyQuery employeeQuery = new WikittyQueryMaker().eq(Employee.ELEMENT_FIELD_EMPLOYEE_COMPANY, company).end();
+
+ WikittyQueryResult<Employee> employeeResult = client.findAllByQuery(Employee.class, employeeQuery);
+
+ List<EmployeeData> employees = new ArrayList<EmployeeData>();
+ for(Employee e : employeeResult.getAll()) {
+ employees.add(new EmployeeData(e, client));
+ }
+ //simply return them lol
+ return renderView("employeeEdit.jsp",
+ "employees",employees,
+ "companies", companies,
+ "company", new CompanyImpl(client.restore(company.getWikittyId())),
+ "title", "Employee edit");
+ }
+
+
+ public Render requestEmployeeEdit(ChoremClient client, String companyId, String addExtension, Call call) {
+ Company company = null;
+
+ if(companyId == null)
+ company = client.getDefaultCompany();
+ else {
+ company = new CompanyImpl(client.restore(companyId));
+ }
+
+ if(call != null) {
+ Wikitty w = client.restore(company.getWikittyId());
+ Map<String, Object> params = call.getExtractParameters();
+ for(String key : params.keySet()) {
+ //Prevent false data
+ if((key.equals("CompanyHR.dailyReturn")
+ || key.equals("CompanyHR.dailyHoursWorked"))) {
+ try {
+ double value = Double.parseDouble(((String[])params.get(key))[0]);
+ w.setFqField(key, value);
+ }
+ catch(java.lang.NumberFormatException e) {
+ System.err.println(e.getMessage());//TODO : user output
+ }
+
+ }
+ }
+
+ client.store(w);
+ }
+
+ if(addExtension != null && addExtension.equals("true")) {
+ Wikitty w = client.restore(company.getWikittyId());
+ w.addExtension(client.restoreExtensionLastVersion("CompanyHR"));
+ client.store(w);
+ }
+
+ return employeeFilter(client, company);
+ }
+
+ public Render editEmployeeValues(ChoremClient client, String employeeId, String salaryStr
+ , String productivityRateStr, String partialTimeStr, String dailyReturnStr) {
+ Wikitty employeeWikitty = client.restore(employeeId);
+ Employee employee = new EmployeeImpl(employeeWikitty);
+ if(!employeeWikitty.hasExtension("EmployeeHR")) {
+ employeeWikitty.addExtension(client.restoreExtensionLastVersion("EmployeeHR"));
+ }
+
+ double salary = 0, productivityRate = 0, partialTime = 0, dailyReturn = 0;
+ List<ErrorJson> errors = new ArrayList<ErrorJson>();
+ try {
+ salary = Double.parseDouble(salaryStr);
+ }
+ catch (java.lang.NumberFormatException e) {
+ errors.add(new ErrorJson("salary", "Salary must be a real number"));
+ }
+
+ try{
+ productivityRate = Double.parseDouble(productivityRateStr);
+ }
+ catch (java.lang.NumberFormatException e) {
+ errors.add(new ErrorJson("productivityRate", "Productivity rate must be a real number"));
+ }
+
+ try {
+ partialTime = Double.parseDouble(partialTimeStr);
+ }
+ catch (java.lang.NumberFormatException e) {
+ errors.add(new ErrorJson("partialTime", "Partial time must be a real number"));
+ }
+
+ try {
+ dailyReturn = Double.parseDouble(dailyReturnStr);
+ }
+ catch (java.lang.NumberFormatException e) {
+ errors.add(new ErrorJson("dailyReturn", "Daily return must be a real number"));
+ }
+
+ if(productivityRate > 100 || productivityRate < 0) {
+ errors.add(new ErrorJson("productivityRate", "Productivity rate must be between 0 and 100"));
+ }
+ if(partialTime > 100 || partialTime < 0) {
+ errors.add(new ErrorJson("partialTime", "Partial time must be between 0 and 100"));
+ }
+ if(errors.size() != 0)
+ return renderJSON("data", "error", "errors", errors);
+
+
+ if(salary != 0)
+ employeeWikitty.setFqField("EmployeeHR.salary", salary);
+ employeeWikitty.setFqField("EmployeeHR.productivityRate", productivityRate);
+ employeeWikitty.setFqField("EmployeeHR.partialTime", partialTime);
+ employeeWikitty.setFqField("EmployeeHR.dailyReturn", dailyReturn);
+ client.store(employeeWikitty);
+
+
+ EmployeeData data = new EmployeeData(new EmployeeImpl(employeeWikitty), client);
+
+ EmployeeJson json = new EmployeeJson(
+ data.getSalary() + "",
+ data.getProductivityRate(),
+ data.getPartialTime(),
+ data.getDailyReturn(),
+ data.getDailyHoursWorked()
+ );
+
+ return renderJSON("data", json);
+
+ }
+ public class ErrorJson {
+ private String errorMessage;
+ private String field;
+
+ public ErrorJson (String field, String message) {
+ this.field = field;
+ this.errorMessage = message;
+ }
+ }
+
+ public class EmployeeJson {
+
+ private String salary;
+ private double productivityRate;
+ private double partialTime;
+ private double dailyReturn;
+ private double dailyHoursWorked;
+
+ public EmployeeJson(String salary, double productivityRate, double partialTime,
+ double dailyReturn, double dailyHoursWorked) {
+ this.salary = salary;
+ this.productivityRate = productivityRate;
+ this.partialTime = partialTime;
+ this.dailyReturn = dailyReturn;
+ this.dailyHoursWorked = dailyHoursWorked;
+ }
+
+ }
+
+ public class EmployeeData {
+
+ private Employee e;
+ private String salary;
+ private double productivityRate;
+ private double partialTime;
+ private double dailyReturn;
+ private double dailyHoursWorked;
+
+
+ public EmployeeData(Employee e, ChoremClient client) {
+ Wikitty w = client.restore(e.getWikittyId());
+ if(w.hasExtension("EmployeeHR")) {
+ this.salary = w.getFieldAsDouble("EmployeeHR", "salary") + "";
+ this.productivityRate = w.getFieldAsDouble("EmployeeHR", "productivityRate");
+ this.partialTime = w.getFieldAsDouble("EmployeeHR", "partialTime");
+ }
+ else {
+ this.salary = "Non renseigné";
+ this.productivityRate = 100;
+ this.partialTime = 100;
+ }
+ this.dailyReturn = client.getDailyReturn(e);
+ this.dailyHoursWorked = client.getDailyHoursWorked(e);
+ this.e = e;
+ }
+
+ public Employee getObject() {
+ return e;
+ }
+
+ public String getSalary() {
+ return salary;
+ }
+
+ public double getDailyReturn() {
+ return dailyReturn;
+ }
+
+ public double getDailyHoursWorked() {
+ return dailyHoursWorked;
+ }
+ public double getProductivityRate() {
+ return productivityRate;
+ }
+ public double getPartialTime() {
+ return partialTime;
+ }
+
+
+
+ }
+
+}
Modified: trunk/chorem-webmotion/src/main/resources/mapping
===================================================================
--- trunk/chorem-webmotion/src/main/resources/mapping 2013-07-22 08:16:20 UTC (rev 372)
+++ trunk/chorem-webmotion/src/main/resources/mapping 2013-07-23 14:51:55 UTC (rev 373)
@@ -11,6 +11,7 @@
* /fragment/* DecoratorFilter.decorate wmDecoratorNo=true
* /sales/funnel/json/* DecoratorFilter.decorate wmDecoratorNo=true
* /project/json/* DecoratorFilter.decorate wmDecoratorNo=true
+* /admin/employeeEdit/json/* DecoratorFilter.decorate wmDecoratorNo=true
* /ascii/* DecoratorFilter.decorate wmDecoratorNo=true
* /rest/* DecoratorFilter.decorate wmDecoratorNo=true
GET /* DecoratorFilter.decorate
@@ -51,6 +52,8 @@
* /fragment/sales/{method} action:sales.SalesAction.{method}
* /admin view:contact.jsp
* /admin/importExport view:admin/importExport.jsp
+* /admin/employeeEdit action:EmployeeEditAction.requestEmployeeEdit
+* /admin/employeeEdit/json/{method}/{employeeId} action:EmployeeEditAction.{method}
* /admin/{method} action:AdminAction.{method}
* /contact view:contact.jsp
* /report view:report.jsp
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp 2013-07-22 08:16:20 UTC (rev 372)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardMultiProject.jsp 2013-07-23 14:51:55 UTC (rev 373)
@@ -56,8 +56,8 @@
<th><abbr title="Taux journalier moyen">TJM</abbr> reel</th>
<th>Gain attendu</th>
<th>Perte/gain</th>
- <th>Average SRP</th>
- <th>Real SRP</th>
+ <th>Average CJM</th>
+ <th>Real CJM</th>
</tr>
</thead>
<c:forEach var="q" items="${quotations}">
@@ -85,9 +85,9 @@
<td class="currency"> <f:formatNumber type="currency"
maxFractionDigits="2" value="${calculations[q].getLossOrProfit()}" /></td>
<td> <f:formatNumber type="number"
- maxFractionDigits="2" value="${calculations[q].getAvgSrp()}" /></td>
+ maxFractionDigits="2" value="${calculations[q].getAvgReturn()}" /></td>
<td> <f:formatNumber type="number"
- maxFractionDigits="2" value="${calculations[q].getRealSrp()}" /></td>
+ maxFractionDigits="2" value="${calculations[q].getRealReturn()}" /></td>
</tr>
</tbody>
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-22 08:16:20 UTC (rev 372)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/decorator.jsp 2013-07-23 14:51:55 UTC (rev 373)
@@ -220,6 +220,8 @@
<li><a href="<c:url value="/admin/importExport"/>">Import/Export</a></li>
<li class="divider"></li>
<li><a href="<c:url value="/admin/reindex"/>">Reindex</a></li>
+ <li class="divider"></li>
+ <li><a href="<c:url value="/admin/employeeEdit"/>">Employee edit</a></li>
</ul>
</li>
</ul>
Added: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp (rev 0)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/employeeEdit.jsp 2013-07-23 14:51:55 UTC (rev 373)
@@ -0,0 +1,112 @@
+<%--
+ #%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="http://java.sun.com/jsp/jstl/fmt" prefix="f"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
+<%@ taglib uri="/WEB-INF/wikitty.tld" prefix="w"%>
+
+<f:setLocale value="${locale}" />
+
+
+
+<h1>${title}</h1>
+
+<form method="GET" id="companyForm">
+
+<select class="filterBox" name="companyId" id="companySelect">
+ <option>Choisir une entreprise</option>
+ <c:forEach var="c" items="${companies}">
+ <option value="${c.wikittyId}"
+ <c:if test="${company.equals(c)}">selected</c:if>
+ >${c.name}</option>
+ </c:forEach>
+ </select>
+</form>
+
+<form class="well form-inline" method="POST" id="company">
+<h4>${company.name}</h4>
+<input type="hidden" name="companyId" value="${company.wikittyId}"/>
+<c:if test='${company.wikitty.hasExtension("CompanyHR")}'>
+Heures par jour : <w:input wikitty="${company.wikitty}" fqfield="CompanyHR.dailyHoursWorked"/>
+CJM : <w:input wikitty="${company.wikitty}" fqfield="CompanyHR.dailyReturn"/>
+<button class="btn btn-success" id="companyBtn">
+ <i class="icon-ok icon-white"></i>
+ Mettre à jour
+ </button>
+</c:if>
+<c:if test='${!company.wikitty.hasExtension("CompanyHR")}'>
+Valeurs par défaut (<a href='variables'>Modifier</a>) :<br/>
+Heures par jour : <input type="test" value="${client.getConfiguration().getDailyHoursWorked()}" disabled/>
+CJM : <input type="test" value="${client.getConfiguration().getDailyReturn()}" disabled/>
+<button class="btn btn-success" id="companyBtn">
+ <i class="icon-plus icon-white"></i>
+ <input type="hidden" name="addExtension" value="true"/>
+ Ajouter extension HR
+ </button>
+</c:if>
+
+</form>
+
+
+<table class="table table-striped table-bordered table-condensed">
+ <thead>
+ <th>Employé</th>
+ <th>Salaire</th>
+ <th>Heures par jour</th>
+ <th>Taux de productivité</th>
+ <th>Temps partiel</th>
+ <th>CJM</th>
+ <th>Edit</th>
+ </thead>
+ <tbody>
+ <c:set var="count" value="0"/>
+ <c:forEach items="${employees}" var="employee">
+ <tr class="row${count}" id="${employee.object.wikittyId}">
+ <td class="person"><w:display wikitty="${employee.object.wikitty}"
+ fqfield="Employee.person" label="" />
+ </td>
+ <td class="salary">
+ ${employee.salary}
+ </td>
+ <td class="dailyHoursWorked"><f:formatNumber type="number"
+ maxFractionDigits="2" value="${employee.dailyHoursWorked}"/></td>
+ <td class="productivityRate"><f:formatNumber type="number"
+ maxFractionDigits="2" value="${employee.productivityRate}"/>%</td>
+ <td class="partialTime"><f:formatNumber type="number"
+ maxFractionDigits="2" value="${employee.partialTime}"/>%</td>
+ <td class="dailyReturn"><f:formatNumber type="number"
+ maxFractionDigits="2" value="${employee.dailyReturn}"/>
+ <a class="cjmRefresh" style="cursor:pointer">
+ <i class="icon icon-refresh"></i></a></td>
+ <td class="rowEdit"><a class="employeeEdit" style="cursor:pointer"><i class="icon icon-edit"></i></a></td>
+
+ </tr>
+ <c:set var="count" value="${count + 1}"/>
+ </c:forEach>
+ </tbody>
+</table>
+
+<script src="<c:url value='/js/employeeEdit.js'/>"></script>
+
+
Added: trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js (rev 0)
+++ trunk/chorem-webmotion/src/main/webapp/js/employeeEdit.js 2013-07-23 14:51:55 UTC (rev 373)
@@ -0,0 +1,110 @@
+
+
+$(document).ready(function() {
+
+
+
+ $("#companySelect").change(function() {
+ $("#companyForm").submit();
+ });
+
+ $(".employeeEdit").click(employeeEdit);
+
+ function employeeEdit() {
+ var row = $(this).parent().parent();
+ var salary = row.find(".salary");
+ var prod = row.find(".productivityRate");
+ var time = row.find(".partialTime");
+ var cjm = row.find(".dailyReturn");
+
+ salary.attr("oldVal", $.trim(salary.html()));
+ salary.html("<input type = 'text' size='3' value='"+$.trim(salary.html())+"'/>");
+ prod.attr("oldVal", $.trim(prod.html()));
+ prod.html("<input type = 'text' size='3' value='"+$.trim(prod.html()).replace('%','')+"'/>");
+ time.attr("oldVal", $.trim(time.html()));
+ time.html("<input type = 'text' size='3' value='"+$.trim(time.html()).replace('%','')+"'/>");
+ cjm.attr("oldVal", $.trim(cjm.html()));
+ cjm.html("<input type = 'text' size='3' value='"+$.trim(cjm.html()).replace('%','')+"'/>");
+
+
+
+
+ $(this).parent().html("<a class='editOk' style='cursor:pointer'><i class='icon icon-ok'></i></a>"
+ + "<a class='editCancel' style='cursor:pointer'><i class='icon icon-remove'></i></a>"
+ );
+ row.find(".editCancel").click(editCancel);
+ row.find(".editOk").click(editOk);
+
+ }
+
+ function editCancel() {
+
+ var row = $(this).parent().parent();
+
+ var salary = row.find(".salary");
+ var prod = row.find(".productivityRate");
+ var time = row.find(".partialTime");
+ var cjm = row.find('.dailyReturn');
+
+ salary.html($.trim(salary.attr('oldVal')));
+ prod.html($.trim(prod.attr('oldVal')));
+ time.html($.trim(time.attr('oldVal')));
+ cjm.html($.trim(cjm.attr('oldVal')));
+
+
+ $(this).parent().html("<a class='employeeEdit' style='cursor:pointer'><i class='icon icon-edit'></i></a>"
+ );
+ row.find(".employeeEdit").click(employeeEdit);
+
+ }
+
+ function editOk() {
+ var row = $(this).parent().parent();
+ var cell = $(this).parent();
+
+ var salary = row.find(".salary");
+ var prod = row.find(".productivityRate");
+ var time = row.find(".partialTime");
+ var cjm = row.find(".dailyReturn");
+
+ var salaryVal = $.trim(salary.find("input").attr('value'));
+ var prodVal = $.trim(prod.find("input").attr('value'));
+ var timeVal = $.trim(time.find("input").attr('value'));
+ var cjmVal = $.trim(cjm.find("input").attr('value'));
+
+ $.get(createUrl("/admin/employeeEdit/json/editEmployeeValues/",row.attr("id"), "?salaryStr=",
+ salaryVal, "&productivityRateStr=", prodVal, "&partialTimeStr=", timeVal,
+ "&dailyReturnStr=", cjmVal),
+ function(ret){
+ console.log(ret);
+ var data = ret['data'];
+
+ if(data === "error") {
+ row.find("input").css("background-color", "white");
+ row.find(".errorMessage").remove();
+ var errors = ret['errors'];
+ for(var i = 0; i < errors.length; i++) {
+ row.find("." + errors[i]["field"]+" input").css("background-color", "#FF5555");
+ row.find("." + errors[i]["field"]).append(
+ "<span class='errorMessage'>"+errors[i]["errorMessage"]+"</span>");
+ }
+ }
+ else
+ {
+ salary.html(data['salary']);
+ prod.html(data["productivityRate"]+"%");
+ time.html(data['partialTime']+"%");
+ cjm.html(data['dailyReturn']);
+ row.find('.dailyHoursWorked').html(data['dailyHoursWorked']);
+ cell.html("<a class='employeeEdit' style='cursor:pointer'><i class='icon icon-edit'></i></a>");
+ }
+
+ row.find(".employeeEdit").click(employeeEdit);
+ }
+ );
+
+
+ }
+
+
+});
\ No newline at end of file
1
0
Author: meynier
Date: 2013-07-22 10:16:20 +0200 (Mon, 22 Jul 2013)
New Revision: 372
Url: http://chorem.org/projects/chorem/repository/revisions/372
Log:
Corrected defaultCompany attribute in Configuration extension
Modified:
trunk/chorem-entities/src/main/xmi/chorem-model.properties
trunk/chorem-entities/src/main/xmi/chorem-model.zargo
Modified: trunk/chorem-entities/src/main/xmi/chorem-model.properties
===================================================================
--- trunk/chorem-entities/src/main/xmi/chorem-model.properties 2013-07-19 14:30:29 UTC (rev 371)
+++ trunk/chorem-entities/src/main/xmi/chorem-model.properties 2013-07-22 08:16:20 UTC (rev 372)
@@ -74,7 +74,9 @@
#
# Configuration
#
-org.chorem.entities.Configuration.class.tagvalue.version=3.0
+org.chorem.entities.Configuration.class.tagvalue.version=4.0
+org.chorem.entities.Configuration.class.tagvalue.preload=Configuration.defaultCompany
+org.chorem.entities.Configuration.attribute.defaultCompany.tagvalue.help=Autocompl\u00e8tion possible
#
# ContactDetails
#
Modified: trunk/chorem-entities/src/main/xmi/chorem-model.zargo
===================================================================
(Binary files differ)
1
0
r371 - in trunk/chorem-webmotion/src/main: java/org/chorem/webmotion/actions/project webapp/WEB-INF/jsp webapp/css webapp/js
by meynier@users.chorem.org 19 Jul '13
by meynier@users.chorem.org 19 Jul '13
19 Jul '13
Author: meynier
Date: 2013-07-19 16:30:29 +0200 (Fri, 19 Jul 2013)
New Revision: 371
Url: http://chorem.org/projects/chorem/repository/revisions/371
Log:
Added project filter and small modifications on the dashboards
Modified:
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java
trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp
trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp
trunk/chorem-webmotion/src/main/webapp/css/jquery.fn.gantt.css
trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js
Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java 2013-07-19 13:50:40 UTC (rev 370)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/DashboardProjectAction.java 2013-07-19 14:30:29 UTC (rev 371)
@@ -1,6 +1,7 @@
package org.chorem.webmotion.actions.project;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
@@ -191,7 +192,7 @@
}
- public Render employeeFilter(ChoremClient client, String id, Date from, Date to) {
+ public Render employeeFilter(ChoremClient client, String id, Date from, Date to, String[] quotationFilters) {
//If no field has been set, sets the date to the actual month
if(from == null || to == null) {
@@ -215,17 +216,29 @@
List<Employee> employeeList = employeeResult.getAll();
//Fetch all the tasks on the interval
- WikittyQuery taskQuery = new WikittyQueryMaker()
- .and()
- .exteq("Task")
- .or()
- .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from, to)
- .bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, from, to)
+ WikittyQueryMaker taskQueryMaker = new WikittyQueryMaker()
.and()
- .le(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from)
- .ge(Interval.FQ_FIELD_INTERVAL_ENDDATE, to)
- .end();
+ .exteq("Task");
+
+ if(quotationFilters!=null) {
+ taskQueryMaker.or();
+ for(String q : quotationFilters) {
+ taskQueryMaker .eq(Task.FQ_FIELD_TASK_QUOTATION,q);
+ System.out.println(client.restore(q));}
+ taskQueryMaker.close();
+ }
+
+ taskQueryMaker .or()
+ .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from, to)
+ .bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, from, to)
+ .and()
+ .le(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from)
+ .ge(Interval.FQ_FIELD_INTERVAL_ENDDATE, to);
+
+ WikittyQuery taskQuery = taskQueryMaker.end();
+ System.out.println(taskQuery);
List<Task> taskList = client.findAllByQuery(Task.class,taskQuery).getAll();
+ System.out.println("RESULT : " + taskList);
//Fetch the employee's workers on those tasks
@@ -280,6 +293,14 @@
for(Employee e : kSet) {
quotations.addAll(timePerQuotation.get(e).keySet());
}
+ List<Quotation> allQuotations = new ArrayList<Quotation>();
+ WikittyQuery quotationQuery = new WikittyQueryMaker().or()
+ .bw(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from, to)
+ .bw(Interval.FQ_FIELD_INTERVAL_ENDDATE, from, to)
+ .and()
+ .le(Interval.FQ_FIELD_INTERVAL_BEGINDATE, from)
+ .ge(Interval.FQ_FIELD_INTERVAL_ENDDATE, to).end();
+ allQuotations = client.findAllByQuery(Quotation.class, quotationQuery).getAll();
HashMap<Employee, HashMap<Quotation, Double>> percentages = new HashMap<Employee, HashMap<Quotation, Double>>();
@@ -307,7 +328,8 @@
"timePerQuotation", timePerQuotation,
"quotations", quotations,
"percentages", percentages,
- "timeTotals", timeTotals);
+ "timeTotals", timeTotals,
+ "allQuotations", allQuotations);
}
@@ -328,9 +350,9 @@
return multiProjectFilter(client, from, to);
}
- public Render requestEmployee(ChoremClient client, String id, Date from, Date to) {
-
- return employeeFilter(client, id, from, to);
+ public Render requestEmployee(ChoremClient client, String id, Date from, Date to, String[] quotations) {
+ System.out.println("QUOTATIONS : " + Arrays.toString(quotations));
+ return employeeFilter(client, id, from, to, quotations);
}
Modified: trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java
===================================================================
--- trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java 2013-07-19 13:50:40 UTC (rev 370)
+++ trunk/chorem-webmotion/src/main/java/org/chorem/webmotion/actions/project/GanttAction.java 2013-07-19 14:30:29 UTC (rev 371)
@@ -66,13 +66,15 @@
else
v = new Values[1];
v[0] = new Values(t.getBeginDate(), t.getEndDate(), t.getName(), customClass, t.getWikittyId());
+
if(t.getDayExtension() != 0) {
GregorianCalendar newEnd = new GregorianCalendar();
newEnd.setTime(t.getEndDate());
newEnd.add(Calendar.DAY_OF_YEAR, (int) t.getDayExtension());
- v[1]= new Values(t.getEndDate(), newEnd.getTime(), "Reestimated end", "ganttOrange", t.getWikittyId());
+ v[1]= new Values(t.getEndDate(), newEnd.getTime(), "Day extension", "ganttOrange", t.getWikittyId());
}
- JTask jt = new JTask(t.getName(), t.getDescription(), t.getWikittyId(),t.getPrice(), t.getEstimatedDays(), v);
+ JTask jt = new JTask(t.getName(), t.getDescription(), t.getWikittyId(),t.getPrice(),
+ t.getEstimatedDays(), t.getDayExtension(), v);
lTask.add(jt);
}
@@ -111,33 +113,36 @@
private class JRender {
- private List<JTask> source;
- private String dateStart;
- private String dateEnd;
- private HashMap<String,String> extDate;
+ private List<JTask> source;
+ private String dateStart;
+ private String dateEnd;
+ private HashMap<String,String> extDate;
public JRender(List<JTask> source,HashMap<String,String> extDate,String dateStart,String dateEnd) {
- this.source = source;
- this.dateStart = dateStart;
- this.dateEnd = dateEnd;
- this.extDate = extDate;
+ this.source = source;
+ this.dateStart = dateStart;
+ this.dateEnd = dateEnd;
+ this.extDate = extDate;
}
}
private class JTask implements Comparable {
- private String name;
- private String desc;
- private Values[] values;
- private String status;
- private String wikittyId;
- private float price;
- private double estimatedDays;
- public JTask(String name, String desc, String wikittyId,float price, double estimatedDays, Values[] values) {
- this.name = name;
- this.desc = desc;
- this.values = values;
- this.wikittyId = wikittyId;
- this.price = price;
- this.estimatedDays = estimatedDays;
+ private String name;
+ private String desc;
+ private Values[] values;
+ private String status;
+ private String wikittyId;
+ private float price;
+ private double estimatedDays;
+ private double dayExtension;
+ public JTask(String name, String desc, String wikittyId,float price,
+ double estimatedDays, double dayExtension, Values[] values) {
+ this.name = name;
+ this.desc = desc;
+ this.values = values;
+ this.wikittyId = wikittyId;
+ this.price = price;
+ this.estimatedDays = estimatedDays;
+ this.dayExtension = dayExtension;
}
@Override
public int compareTo(Object task) {
@@ -147,17 +152,17 @@
}
private class Values implements Comparable{
- private String from;
- private String to;
- private String label;
- private String customClass;
- private String dataObj;
+ private String from;
+ private String to;
+ private String label;
+ private String customClass;
+ private String dataObj;
public Values(Date from, Date to, String label, String customClass, String dataObj) {
- this.from = "/Date(" + from.getTime() + ")/";
- this.to = "/Date(" + to.getTime() + ")/";
- this.label = label;
- this.customClass = customClass;
- this.dataObj = dataObj;
+ this.from = "/Date(" + from.getTime() + ")/";
+ this.to = "/Date(" + to.getTime() + ")/";
+ this.label = label;
+ this.customClass = customClass;
+ this.dataObj = dataObj;
}
@Override
public int compareTo(Object o) {
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp 2013-07-19 13:50:40 UTC (rev 370)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardEmployee.jsp 2013-07-19 14:30:29 UTC (rev 371)
@@ -49,6 +49,17 @@
</div>
</div>
+<div style="max-height:50px;background-color:#EEEEEE;padding:15px;margin:0px;overflow:auto;">
+ <c:forEach items="${allQuotations}" var="q">
+ <input type="checkbox" name="quotations"
+ <c:if test="${empty param.quotations or quotations.contains(q)}">
+ checked="yes"
+ </c:if>
+ value="${q.wikittyId}"/> ${q.description}
+ </c:forEach>
+
+</div>
+
</form>
<table class="table table-striped table-bordered table-condensed">
Modified: trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-19 13:50:40 UTC (rev 370)
+++ trunk/chorem-webmotion/src/main/webapp/WEB-INF/jsp/dashboardSingleProject.jsp 2013-07-19 14:30:29 UTC (rev 371)
@@ -165,8 +165,8 @@
<th>TJM réel</th>
<th>Gain attendu</th>
<th>Gain/perte</th>
- <th>SRP moyen</th>
- <th>SRP réel</th>
+ <th>CJM moyen</th>
+ <th>CJM réel</th>
</tr>
</thead>
<tbody>
@@ -202,9 +202,9 @@
maxFractionDigits="2"
value="${task.getLossOrProfit()}" /></td>
<td class="number"><f:formatNumber type="number"
- maxFractionDigits="2" value="${task.getAvgSrp()}" /></td>
+ maxFractionDigits="2" value="${task.getAvgReturn()}" /></td>
<td class="number"><f:formatNumber type="number"
- maxFractionDigits="2" value="${task.getRealSrp()}" /></td>
+ maxFractionDigits="2" value="${task.getRealReturn()}" /></td>
</tr>
Modified: trunk/chorem-webmotion/src/main/webapp/css/jquery.fn.gantt.css
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/css/jquery.fn.gantt.css 2013-07-19 13:50:40 UTC (rev 370)
+++ trunk/chorem-webmotion/src/main/webapp/css/jquery.fn.gantt.css 2013-07-19 14:30:29 UTC (rev 371)
@@ -55,7 +55,7 @@
display: inline-block;
margin: 0 0 0 5px;
color: #484A4D;
- width: 110px;
+ width: 220px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
@@ -73,13 +73,10 @@
}
.fn-gantt .leftPanel .name {
- width: 110px;
+ width: 220px;
font-weight: bold;
}
-.fn-gantt .leftPanel .desc {
- width: 115px;
-}
.fn-gantt .leftPanel .fn-wide, .fn-gantt .leftPanel .fn-wide .fn-label {
width: 225px;
Modified: trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js
===================================================================
--- trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js 2013-07-19 13:50:40 UTC (rev 370)
+++ trunk/chorem-webmotion/src/main/webapp/js/jquery.fn.gantt.js 2013-07-19 14:30:29 UTC (rev 371)
@@ -369,13 +369,15 @@
$.each(element.data, function (i, entry) {
if (i >= element.pageNum * settings.itemsPerPage && i < (element.pageNum * settings.itemsPerPage + settings.itemsPerPage)) {
entries.push('<div class="row name row' + i + (entry.desc ? '' : ' fn-wide')
- + '" id="rowheader' + i + '" offset="'
+ + '" id="rowheader' + i + '" over="'
+ + "<b>" + entry.name + "</b><br/>" + entry.desc
+ +'" offset="'
+ i % settings.itemsPerPage * tools.getCellSize() + '" dataObj="'
+ entry.wikittyId + '">');
entries.push('<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry.name + '</span>');
entries.push('</div>');
- if (entry.desc) {
+ /*if (entry.desc) {
entries.push('<div class="row desc row' + i + ' " id="RowdId_' + i + '" data-id="'
+ entry.id + '" dataObj="'
+ entry.wikittyId + '">');
@@ -383,14 +385,13 @@
entries.push('<span class="fn-label' + (entry.cssClass ? ' ' + entry.cssClass : '') + '">' + entry.desc + '</span>');
entries.push('</div>');
- }
+ }*/
}
});
ganttLeftPanel.append(entries.join(""));
ganttLeftPanel.find('.row:not(.spacer)').mouseover(core.mover)
.mouseout(core.mout)
.mousemove(core.mmove)
- .attr('over', 'text')
.click(function (e) {
e.stopPropagation();
settings.onItemClick($(this).attr("dataObj"));
@@ -1005,7 +1006,7 @@
// **Progress Bar**
// Return an element representing a progress of position within
// the entire chart
- createProgressBar: function (days, cls, desc, label,price, estimatedDays, dataObj) {
+ createProgressBar: function (days, cls, desc, label,price, estimatedDays,dayExtension, dataObj) {
var cellWidth = tools.getCellSize();
var barMarg = tools.getProgressBarMargin() || 0;
@@ -1022,8 +1023,9 @@
var hint;
var str;
- hint = $('<div class="fn-gantt-hint"/>').html(label + " :<br/>"
- + desc + "<br/>Price : " + price + "<br />Estimated days : " + estimatedDays);
+ hint = $('<div class="fn-gantt-hint"/>').html("<b>" + label + "</b><br/>"
+ + desc + "<br/>Price : " + price + "<br />Estimated days : " + estimatedDays
+ + "<br/>Day extension : " + dayExtension);
$("body").append(hint);
hint.css("left", e.pageX);
hint.css("top", e.pageY);
@@ -1179,6 +1181,7 @@
day.label ? day.label : "",
entry.price ? entry.price : "",
entry.estimatedDays ? entry.estimatedDays : "",
+ entry.dayExtension ? entry.dayExtension : "0",
day.dataObj ? day.dataObj : null
);
@@ -1224,6 +1227,7 @@
day.label ? day.label : "",
entry.price ? entry.price : "",
entry.estimatedDays ? entry.estimatedDays : "",
+ entry.dayExtension ? entry.dayExtension : "0",
day.dataObj ? day.dataObj : null
);
@@ -1266,6 +1270,7 @@
day.label ? day.label : "",
entry.price ? entry.price : "",
entry.estimatedDays ? entry.estimatedDays : "",
+ entry.dayExtension ? entry.dayExtension : "0",
day.dataObj ? day.dataObj : null
);
@@ -1294,6 +1299,7 @@
day.label ? day.label : "",
entry.price ? entry.price : "",
entry.estimatedDays ? entry.estimatedDays : "",
+ entry.dayExtension ? entry.dayExtension : "0",
day.dataObj ? day.dataObj : null
);
1
0
19 Jul '13
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(a)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>
1
0
r369 - in trunk/chorem-entities/src/main: java/org/chorem java/org/chorem/project xmi
by meynier@users.chorem.org 18 Jul '13
by meynier@users.chorem.org 18 Jul '13
18 Jul '13
Author: meynier
Date: 2013-07-18 16:49:49 +0200 (Thu, 18 Jul 2013)
New Revision: 369
Url: http://chorem.org/projects/chorem/repository/revisions/369
Log:
Updated ChoremClient to match the new model
Modified:
trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java
trunk/chorem-entities/src/main/java/org/chorem/project/Calculation.java
trunk/chorem-entities/src/main/xmi/chorem-model.properties
Modified: trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java
===================================================================
--- trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java 2013-07-18 13:28:33 UTC (rev 368)
+++ trunk/chorem-entities/src/main/java/org/chorem/ChoremClient.java 2013-07-18 14:49:49 UTC (rev 369)
@@ -397,9 +397,17 @@
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Wikitty w = this.restore(e.getWikittyId());
- if(w.hasExtension("Contract") && w.getFieldAsDouble("Contract", "dailyReturn") != 0) {
+ Wikitty companyW = null;
+ if(e.getCompany(false) != null)
+ companyW = restore(e.getCompany(false).getWikittyId());
+
+ if(w.hasExtension("EmployeeHR") && w.getFieldAsDouble("EmployeeHR", "dailyReturn") != 0) {
return w.getFieldAsDouble("Contract", "dailyReturn");
}
+ else if(companyW != null && companyW.hasExtension("CompanyHR")
+ && companyW.getFieldAsDouble("CompanyHR", "dailyReturn") != 0) {
+ return companyW.getFieldAsDouble("CompanyHR", "dailyReturn");
+ }
else {
return this.getConfiguration().getDailyReturn();
}
@@ -413,10 +421,20 @@
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//Code commented before confirmation or modification of the contract implementation
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ Wikitty w = this.restore(e.getWikittyId());
+ Wikitty companyW = null;
+ if(e.getCompany(false) != null)
+ companyW = restore(e.getCompany(false).getWikittyId());
- Wikitty w = this.restore(e.getWikittyId());
- if(w.hasExtension("Contract") && w.getFieldAsDouble("Contract", "dailyHoursWorked") != 0) {
- return w.getFieldAsDouble("Contract", "dailyHoursWorked");
+ if(companyW.hasExtension("CompanyHR") && companyW.getFieldAsDouble("CompanyHR", "dailyHoursWorked") != 0) {
+ if(w.hasExtension("EmployeeHR") && w.getFieldAsDouble("EmployeeHR", "partialTime") != 0) {
+ return companyW.getFieldAsDouble("CompanyHR", "dailyHoursWorked") *
+ (w.getFieldAsDouble("EmployeeHR", "partialTime")/100);
+ }
+ else {
+ return companyW.getFieldAsDouble("CompanyHR", "dailyHoursWorked");
+ }
+
}
else {
return this.getConfiguration().getDailyHoursWorked();
Modified: trunk/chorem-entities/src/main/java/org/chorem/project/Calculation.java
===================================================================
--- trunk/chorem-entities/src/main/java/org/chorem/project/Calculation.java 2013-07-18 13:28:33 UTC (rev 368)
+++ trunk/chorem-entities/src/main/java/org/chorem/project/Calculation.java 2013-07-18 14:49:49 UTC (rev 369)
@@ -36,8 +36,8 @@
private Double expectedProfit = null;
private Double lossOrProfit = null;
private Double resultPerDay = null;
- private Double avgReturn = null;
- private Double realReturn = null;
+ private Double avgReturn = null;
+ private Double realReturn = null;
/**
Modified: trunk/chorem-entities/src/main/xmi/chorem-model.properties
===================================================================
--- trunk/chorem-entities/src/main/xmi/chorem-model.properties 2013-07-18 13:28:33 UTC (rev 368)
+++ trunk/chorem-entities/src/main/xmi/chorem-model.properties 2013-07-18 14:49:49 UTC (rev 369)
@@ -69,8 +69,8 @@
#
# CompanyHR
#
-org.chorem.entities.Company.class.tagvalue.version=1.0
-org.chorem.entities.Company.class.tagvalue.toString=%Company.name|noname$s
+org.chorem.entities.CompanyHR.class.tagvalue.version=1.0
+org.chorem.entities.CompanyHR.class.tagvalue.toString=%Company.name|noname$s
#
# Configuration
#
@@ -114,6 +114,7 @@
org.chorem.entities.EmployeeHR.attribute.dailyReturn.tagvalue.help=Seuil de rentabilite productif
org.chorem.entities.EmployeeHR.attribute.productivityRate.tagvalue.help=Taux de productivité
org.chorem.entities.EmployeeHR.attribute.partialTime.tagvalue.help=Taux de travail (temps complet:100, mi-temps:50)
+org.chorem.entities.EmployeeHR.attribute.partialTime.tagvalue.default=100
#
# Evaluation (a mettre ici)
#
1
0