Table of contents
No headers
/**
Author: Blake Harms
Version 2.9
See: http://developer.mindtouch.com/App_Catalog/Integrated_Bug_and_Issue_Tracker
on 2.9 added performance tunning posted by Sego on this blog post: http://forums.developer.mindtouch.com/showthread.php?5882-Integrated-Bug-tracker-Support-Ticket-Library-with-JQuery&p=45759#post45759
*/
// ~~ Parameters ~~
var parameters = $0 ?? $options ?? [
{'title': {label:'Title',type:'text'}},
{'type': {data:['Bug','Support'],label:'Type',show:'both'}},
{'urgency': {data:['Low','Medium','High'],label:'Urgency',show:'both'}},
{'assign': {data:[u.name foreach var u in site.users where !u.anonymous],label:'Assign',show:'both', type:'suggest'}},
{'status': {data:{'#FBB':'Open','#FBF':'Assigned','#CCF':'Fix Issued','#DDD':'Closed','#AFA':'Fixed'},label:'Status'}},
{'summary': {label:'Summary',type:'textarea', show:'ticket'}},
{'time': {data:date.now,type:'hidden',show:'table'}}
];
// -- Added by carles.coll 2010/12/27
var new_page_content = $1 ?? $new_page_content ?? '<pre class=\\'script\\'> Ticket() </pre>';
var params = [];
foreach(var param in parameters){
let params ..= Map.keyValues(param);
}
if(! list.contains(list.collect(params,'key'),'title')){
<div style="font-weight:bold;color:red;">"Warning: A title field must be assigned."</div>
}
//dekiapi();
jquery.ui("smoothness");
var options = "{'params':'" .. String.serialize(params) .. "'}";
var updateOptions = false;
if(page.properties.options.text != options){
let updateOptions=true;
let options = String.replace(String.replace(options,'"','\\"'),"'","\\'");
}
<html><head>
<script type="text/javascript" src="http://www.datatables.net/media/javascript/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="http://jqplugins.appspot.com/js/jquery.table2csv.js"></script>
<script type="text/javascript" src="http://developer.mindtouch.com/@api/deki/files/4634/=jquery.autocomplete.pack.js"></script>
<link rel="stylesheet" type="text/css" href="http://developer.mindtouch.com/@api/deki/files/4635/=jquery.autocomplete.css" />
<script type="text/javascript">"
var oTable;
Deki.$(document).ready(function(){
oTable = Deki.$('#"..@tickets.."').dataTable();
Deki.$('table.display tfoot select').change(filter);
Deki.$('table.display tfoot input').keyup(filter);
Deki.$('.datepicker').datepicker();
Deki.$('.resizable').resizable();
});
"</script>
if(updateOptions){
<script type="text/javascript">"
// Set page property 'options' with the options submitted to this template.
var prop = 'urn:custom.mindtouch.com#' + 'options'; // url that retrieves the ticket options
Deki.Api.ReadPageProperty(null, prop, function(result) {
if(result.etag) { // page property exists, write over it.
Deki.Api.UpdatePageProperty(result.href, '"..options.."', result.etag, function() {
}, function(result) {
// alert('An error occurred trying to update the store (status: ' + result.status + ' - ' + result.text + ')');
});
} else { // page property doesn't exist, create one.
Deki.Api.CreatePageProperty(null, prop, '"..options.."', null,null);
}
}, function(result) {
//alert('An error occurred trying to read the store (status: ' + result.status + ' - ' + result.text + ')');
});
"</script>
}
<script type="text/javascript">"
var filter= function() {
Deki.$('table.display tfoot select, table.display tfoot input').each(function() {
oTable.fnFilter(Deki.$(this).val(),$('table.display tfoot th').index(Deki.$(this).parent()));
});
}
var saveTicket= function(page, properties){
// Hide the ability to change stuff.
Deki.$('#saveButton').attr('value','Saving...').attr('disabled','disabled');
Deki.$('#" .. @form .. " tbody').find('input,select,textarea').each(function(){
$(this).attr('disabled','disabled');
});
saveProperty(page,properties); // recursive...
}
var saveProperty= function(page, properties){ // NOTE: This function is recursive. It will call itself for all of properties.
var propertyname;
var propertytext;
var first = false;
var theRest= new Array(); // the rest of the array.
for(var key in properties){
if(! first){
first = true;
propertyname = key;
propertytext = properties[key];
}
else {
theRest[key] = properties[key];
theRest.length++;
}
}
var pageapi = '/@api/deki/pages/=' + Deki.url.encode(Deki.url.encode(page));
// no need to check for update as we just created this page.
Deki.Api.CreatePageProperty(pageapi, 'urn:custom.mindtouch.com#' + propertyname, propertytext, function() {
if(theRest.length > 0){
saveProperty(page,theRest); // recurse
}
else{
location.href = '/' + page;
}
}, function(result) {
// just ignore it...
alert('An error occurred trying to create the store (status: ' + result.status + ' - ' + result.text + ')');
});
}
"</script>
<style type="text/css">"
/* Ticket Table CSS */
.dataTables_wrapper { position: relative; min-height: 302px; _height: 302px; clear: both; }
.dataTables_processing { position: absolute; top: 0px; left: 50%; width: 250px; margin-left: -125px; border: 1px solid #ddd; text-align: center; color: #999; font-size: 11px; padding: 2px 0; }
.dataTables_length { width: 40%; float: left; }
.dataTables_filter { width: 50%; float: right; text-align: right; }
.dataTables_info { width: 60%; float: left; }
.dataTables_paginate { float: right; text-align: right;padding:10px; }
/* DataTables display */
table.display { margin: 0 auto; clear: both;width:100%; }
table.display thead th { padding: 3px 10px; border-bottom: 1px solid black; font-weight: bold; cursor: pointer; _cursor: hand;text-align:center; }
table.display tfoot th { padding: 3px 10px; border-top: 1px solid black; font-weight: bold;text-align:center; }
table.display tr.heading2 td { border-bottom: 1px solid #aaa; }
table.display td { padding: 3px 10px; text-align:center }
table.display td.center { text-align: center; }
/* sorting */
.sorting_asc { background: url('http://www.datatables.net/media/images/sort_asc.jpg') no-repeat center right; }
.sorting_desc { background: url('http://www.datatables.net/media/images/sort_desc.jpg') no-repeat center right; }
.sorting { background: url('http://www.datatables.net/media/images/sort_both.jpg') no-repeat center right; }
/* paginate */
.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next { height: 19px; width: 19px; margin-left: 3px; float: left; }
.paginate_disabled_previous { background-image: url('http://www.datatables.net/media/images/back_disabled.jpg'); }
.paginate_enabled_previous { background-image: url('http://www.datatables.net/media/images/back_enabled.jpg'); }
.paginate_disabled_next { background-image: url('http://www.datatables.net/media/images/forward_disabled.jpg'); }
.paginate_enabled_next { background-image: url('http://www.datatables.net/media/images/forward_enabled.jpg'); }
/* Pagination nested */
.paginate_button, .paginate_active { border:1px solid black; padding:0px 3px; text-align:center; -moz-border-radius:3px;margin:3px;font-size:12px; } .paginate_button { background-color: #F0F0F0; cursor:pointer; _cursor:hand; }
.paginate_active { font-weight:bold;background-color: #DDD; }
table.display tr.odd { background-color: #F0F0F0; }
table.display tr.even { background-color: white; }
"</style>
if(list.contains(list.collect(params,'key'),'status')){
<style type="text/css">"
/* Ticket Table CSS */
.new { background-color: #FFFFA7 !important; }
"</style>
var status = list.collect(params,'value')[list.indexof(list.collect(params,'key'),'status')]; // This is absolutely wicked. Is there a better way?
<style type='text/css'>
foreach(var s in Map.keyvalues(status.data)){
"."..String.replace(String.ToLower(s.value),' ','').." { background-color:"..s.key.." !important; }";
}
</style>
}
</head></html>
<form id=(@submit)>
<table class="submitform" cellspacing="0" cellpadding="5" border="1" id=(@form)>
<tbody>
foreach(var param in params){
if(param.value.type=='hidden') continue; // skip the hidden values.
<tr>
<td> param.value.label ?? String.ToCamelCase(param.key)</td>
// check for type variable. if non-existant, determine based on data.
var type= param.value.type;
if(! type){
if(param.value.data is list || param.value is map) let type = 'select';
if(!param.value.data || param.value.data is str) let type = 'text';
}
<td>
switch(type){
case 'suggest':
if(param.value.data is list){
<input type="text" name=(String.toLower(String.replace(param.key,' ','_'))) style=(param.value.style.form) value=(param.value.data) ctor=(
"$this.autocomplete("..JSON.emit(param.value.data)..");") />
}
break;
case 'text':
<input type="text" name=(String.toLower(String.replace(param.key,' ','_'))) style=(param.value.style.form) value=(param.value.data) />
break;
case 'textarea':
var resizable = (typeof param.value.resizable == typeof null || param.value.resizable) ? 'resizable' : '';
<textarea class=(resizable) name=(String.toLower(String.replace(param.key,' ','_'))) style=(param.value.style.form)>param.value.data</textarea>
break;
case 'datepicker':
<input type="text" class="datepicker" name=(String.toLower(String.replace(param.key,' ','_'))) style=(param.value.style.form) value=(param.value.data) />
break;
case 'select':
if((param.value.data is map) || (param.value.data is list)){
<select name=(String.toLower(String.replace(param.key,' ','_'))) style=(param.value.style.form)>
<option value=""> "Select..."</option>
foreach(var option in param.value.data){
<option value=(option)> option </option>
}
</select>
}
break;
default:
// ignore.
break;
}
</td>
</tr>
}
<tr>
<td>
foreach(var param in params){
if(param.value.type!='hidden') continue;
<input type="hidden" name=(String.toLower(String.replace(param.key,' ','_'))) value=(param.value.data) />
}
</td>
<td align="right">
<input type="button" value="Submit" ctor="when($this.click) {
var m = { };
Deki.$('#' + {{@form}}).find('input, select, textarea').each(function() {
m[this.name] = Deki.$(this).val();
});
Deki.publish('default', m);
}" id="saveButton" />
</td>
</tr>
</tbody>
</table>
</form>
<script type="text/jem">"
var dapi = '/@api/deki/pages/=';
var dparams = '/contents?abort=never';
var dpath;
Deki.subscribe('default', null, function(c, m, d) { // listen for submit events.
// -- Modified by carles.coll 2010/12/27
// dpath = Deki.url.encode(M'"..page.path.."' + '/' + m['title'].replace(/ /g,'_'));*/
dpath = Deki.url.encode(MindTouch.Text.Utf8Encode('"..page.path.."' + '/' + m['title'].replace(/ /g,'_')));
var edpath = Deki.url.encode(Deki.url.encode(dpath));
// -- Modified by carles.coll 2010/12/27
// var ddata = ''<pre class=\\'script\\'> Ticket() </pre>'';
var ddata = '"..new_page_content.."';
Deki.$.ajax({
type: 'POST',
url: dapi+edpath+dparams,
data: ddata,
complete: function(xhr){
if(xhr.status == 200) saveTicket(dpath, m);
else if(xhr.status == 400) alert('Request already exists! Try again with a different title.');
else if(xhr.status == 403) alert('Permission denied. Log in and try again.');
else alert('Error ' + xhr.status);
}
});
}, null);
"</script>
<br /><br />
<div id="buttons-panel">
<input type="button" value="Export to CSV" ctor="
var self = $this;
$this.click(function() {
$({{'#'..@download}}).table2csv({
callback: function (csv, name) {
$('<div></div>')
.append($('<textarea></textarea>')
.css({'width':'500px','height':'400px'})
.text(csv))
.insertAfter('#buttons-panel')
.dialog({
height: 480,
width: 560
});
}
});
});
" />
<input type='button' value='Clear Filters' onclick="
Deki.$('table.display tfoot select, table.display tfoot input').each(function() {
Deki.$(this).val('');
oTable.fnFilter(Deki.$(this).val(),$('table.display tfoot th').index(Deki.$(this).parent()));
});
" />
</div>
<br /><br />
var data = page.subpages;
if(#data > 0) {
<table class="display" id=(@tickets) cellpadding="3" cellspacing="0" width="100%">
<thead>
<tr>
foreach(var param in params){
if(! param.value.show || param.value.show == 'both' || param.value.show == 'table'){
<th> param.value.label ?? String.ToCamelCase(param.key) </th>
}
}
</tr>
</thead>
<tbody>
foreach(var p in data){
var class="";
var props = p.properties;
if(list.contains(list.collect(params,'key'),'status')){
if(props.status.text && props.status.text != ''){
let class = String.replace(String.toLower(props.status.text)," ","");
}
else{
let class = "new";
}
}
<tr id=(props.id.text) class=(class)>
foreach(var param in params){
if(param.value.show && param.value.show == 'ticket') continue; // if not supposed to show in table, skip.
var property = Map.values(map.select(props, "$.key=='"..String.replace(String.toLower(param.key)," ","_").."'"))[0];
<td>
switch (param.value.type){
case 'datepicker':
if(param.key =='title'){
<a href=(p.uri) style=(param.value.style.table)>property.text</a>
}else{
<span style=(param.value.style.table)> property.text </span>
}
break;
case 'textarea':
<span style="display:none;"> property.text </span> // keep this here so that filtering sees the whole string, not just the first 50 chars.
if(#property.text >50){ // A string of over 50 characters is long. Don't show the whole thing.
if(param.key =='title'){
<a href=(p.uri) style=(param.value.style.table)>String.Substr(property.text,0,50).."..."</a>
}else{
<span style=(param.value.style.table)>String.Substr(property.text,0,50).."..."</span>
}
}
else{
if(param.key =='title'){
<a href=(p.uri) style=(param.value.style.table)>property.text</a>
}else{
<span style=(param.value.style.table)>property.text</span>
}
}
break;
default:
if(param.key =='title'){
<a href=(p.uri) style=(param.value.style.table)>property.text </a>
}else{
<span style=(param.value.style.table)>property.text</span>
}
break;
}
</td>
}
</tr>
}
</tbody>
<tfoot>
<tr>
// Allow filtering for lists and maps.
foreach(var param in params){
if(param.value.show && param.value.show == 'ticket') continue; //skip if not supposed to show in table.
if(param.value.data is list || param.value.data is map){
<th>
<select>
<option value=""> "Show only..." </option>
foreach(var option in param.value.data){
<option value=(option)>(option)</option>
}
</select>
</th>
}
else {
<th>
<input type="text" />
</th>
}
}
</tr>
</tfoot>
</table>
<table id=(@download) style="display:none">
<tbody>
<tr>
foreach(var param in params){
<td> param.value.label ?? String.ToCamelCase(param.key) </td>
}
</tr>
foreach(var p in data){
<tr>
var props = p.properties;
foreach(var param in params){
var property = Map.values(map.select(props, "$.key=='"..String.replace(String.toLower(param.key)," ","_").."'"));
let property = property[0];
<td>property.text</td>
}
</tr>
}
</tbody>
</table>
}
else {
"Table contains no data.";
}

Comments