Line 0
Link Here
|
|
|
1 |
[% INCLUDE 'doc-head-open.inc' %] |
2 |
<title>Koha › Tools › Quote Uploader</title> |
3 |
[% INCLUDE 'doc-head-close.inc' %] |
4 |
<link rel="stylesheet" type="text/css" href="/intranet-tmpl/prog/en/css/uploader.css" /> |
5 |
<link rel="stylesheet" type="text/css" href="/intranet-tmpl/prog/en/css/datatables.css" /> |
6 |
<script type="text/javascript" src="/intranet-tmpl/prog/en/lib/jquery/plugins/jquery.dataTables.min.js"></script> |
7 |
[% INCLUDE 'datatables-strings.inc' %] |
8 |
</script> |
9 |
<script type="text/javascript" src="/intranet-tmpl/prog/en/js/datatables.js"></script> |
10 |
<script type="text/javascript" src="/intranet-tmpl/prog/en/js/jquery.jeditable.mini.js"></script> |
11 |
<script type="text/javascript"> |
12 |
//<![CDATA[ |
13 |
var oTable; //DataTable object |
14 |
$(document).ready(function() { |
15 |
|
16 |
// Credits: |
17 |
// FileReader() code copied and hacked from: |
18 |
// http://www.html5rocks.com/en/tutorials/file/dndfiles/ |
19 |
// fnCSVToArray() gratefully borrowed from: |
20 |
// http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm |
21 |
|
22 |
var reader; |
23 |
var progress = document.querySelector('.percent'); |
24 |
$("#server_response").hide(); |
25 |
|
26 |
function fnAbortRead() { |
27 |
reader.abort(); |
28 |
} |
29 |
|
30 |
function fnErrorHandler(evt) { |
31 |
switch(evt.target.error.code) { |
32 |
case evt.target.error.NOT_FOUND_ERR: |
33 |
alert('File Not Found!'); |
34 |
break; |
35 |
case evt.target.error.NOT_READABLE_ERR: |
36 |
alert('File is not readable'); |
37 |
break; |
38 |
case evt.target.error.ABORT_ERR: |
39 |
break; // noop |
40 |
default: |
41 |
alert('An error occurred reading this file.'); |
42 |
}; |
43 |
} |
44 |
|
45 |
function fnUpdateProgress(evt) { |
46 |
// evt is an ProgressEvent. |
47 |
if (evt.lengthComputable) { |
48 |
var percentLoaded = Math.round((evt.loaded / evt.total) * 100); |
49 |
// Increase the progress bar length. |
50 |
if (percentLoaded < 100) { |
51 |
progress.style.width = percentLoaded + '%'; |
52 |
progress.textContent = percentLoaded + '%'; |
53 |
} |
54 |
} |
55 |
} |
56 |
|
57 |
function fnCSVToArray( strData, strDelimiter ){ |
58 |
// This will parse a delimited string into an array of |
59 |
// arrays. The default delimiter is the comma, but this |
60 |
// can be overriden in the second argument. |
61 |
|
62 |
// Check to see if the delimiter is defined. If not, |
63 |
// then default to comma. |
64 |
strDelimiter = (strDelimiter || ","); |
65 |
|
66 |
// Create a regular expression to parse the CSV values. |
67 |
var objPattern = new RegExp( |
68 |
( |
69 |
// Delimiters. |
70 |
"(\\" + strDelimiter + "|\\r?\\n|\\r|^)" + |
71 |
// Quoted fields. |
72 |
"(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + |
73 |
// Standard fields. |
74 |
"([^\"\\" + strDelimiter + "\\r\\n]*))" |
75 |
), |
76 |
"gi" |
77 |
); |
78 |
|
79 |
// Create an array to hold our data. Give the array |
80 |
// a default empty first row. |
81 |
var arrData = [[]]; |
82 |
|
83 |
// Create an array to hold our individual pattern |
84 |
// matching groups. |
85 |
var arrMatches = null; |
86 |
|
87 |
// Keep looping over the regular expression matches |
88 |
// until we can no longer find a match. |
89 |
while (arrMatches = objPattern.exec( strData )){ |
90 |
|
91 |
// Get the delimiter that was found. |
92 |
var strMatchedDelimiter = arrMatches[ 1 ]; |
93 |
|
94 |
// Check to see if the given delimiter has a length |
95 |
// (is not the start of string) and if it matches |
96 |
// field delimiter. If it does not, then we know |
97 |
// that this delimiter is a row delimiter. |
98 |
if ( strMatchedDelimiter.length && (strMatchedDelimiter != strDelimiter) ){ |
99 |
// Since we have reached a new row of data, |
100 |
// add an empty row to our data array. |
101 |
// Note: if there is not more data, we will have to remove this row later |
102 |
arrData.push( [] ); |
103 |
} |
104 |
|
105 |
// Now that we have our delimiter out of the way, |
106 |
// let's check to see which kind of value we |
107 |
// captured (quoted or unquoted). |
108 |
if (arrMatches[ 2 ]){ |
109 |
// We found a quoted value. When we capture |
110 |
// this value, unescape any double quotes. |
111 |
var strMatchedValue = arrMatches[ 2 ].replace( |
112 |
new RegExp( "\"\"", "g" ), |
113 |
"\"" |
114 |
); |
115 |
} else if (arrMatches[3]){ |
116 |
// We found a non-quoted value. |
117 |
var strMatchedValue = arrMatches[ 3 ]; |
118 |
} else { |
119 |
// There is no more valid data so remove the row we added earlier |
120 |
// Is there a better way? Perhaps a look-ahead regexp? |
121 |
arrData.splice(arrData.length-1, 1); |
122 |
} |
123 |
|
124 |
// Now that we have our value string, let's add |
125 |
// it to the data array. |
126 |
arrData[ arrData.length - 1 ].push( strMatchedValue ); |
127 |
} |
128 |
|
129 |
// Return the parsed data. |
130 |
return( arrData ); |
131 |
} |
132 |
|
133 |
function fnDataTable(aaData) { |
134 |
for(var i=0; i<aaData.length; i++) { |
135 |
aaData[i].push('Delete'); //this is hackish FIXME |
136 |
} |
137 |
document.getElementById('quotes_editor').style.visibility="visible"; |
138 |
document.getElementById('file_uploader').style.visibility="hidden"; |
139 |
oTable = $('#quotes_editor').dataTable( { |
140 |
"bAutoWidth" : false, |
141 |
"bPaginate" : true, |
142 |
"bSort" : false, |
143 |
"sPaginationType" : "full_numbers", |
144 |
"sDom" : '<"save_quotes">frtip', |
145 |
"aaData" : aaData, |
146 |
"aoColumns" : [ |
147 |
{ |
148 |
"sTitle" : "Source", |
149 |
"sWidth" : "15%", |
150 |
}, |
151 |
{ |
152 |
"sTitle" : "Quote", |
153 |
"sWidth" : "75%", |
154 |
}, |
155 |
{ |
156 |
"sTitle" : "Actions", |
157 |
"sWidth" : "10%", |
158 |
}, |
159 |
], |
160 |
"fnPreDrawCallback": function(oSettings) { |
161 |
return true; |
162 |
}, |
163 |
"fnRowCallback": function( nRow, aData, iDisplayIndex ) { |
164 |
noEditFields = [2]; /* action */ |
165 |
/* console.log('Quote ID: '+quoteID); */ |
166 |
/* do foo on various cells in the current row */ |
167 |
$('td:eq(2)', nRow).html('<input type="button" class="delete" value="Delete" onclick="fnClickDeleteRow(this.parentNode);" />'); |
168 |
/* apply no_edit id to noEditFields */ |
169 |
for (i=0; i<noEditFields.length; i++) { |
170 |
$('td', nRow)[noEditFields[i]].setAttribute("id","no_edit"); |
171 |
} |
172 |
return nRow; |
173 |
}, |
174 |
"fnDrawCallback": function(oSettings) { |
175 |
/* Apply the jEditable handlers to the table on all fields w/o the no_edit id */ |
176 |
$('#quotes_editor tbody td[id!="no_edit"]').editable( function(value, settings) { |
177 |
var cellPosition = oTable.fnGetPosition( this ); |
178 |
oTable.fnUpdate(value, cellPosition[0], cellPosition[1], false, false); |
179 |
return(value); |
180 |
}, |
181 |
{ |
182 |
"callback" : function( sValue, y ) { |
183 |
oTable.fnDraw(false); /* no filter/sort or we lose our pagination */ |
184 |
}, |
185 |
"height" : "14px", |
186 |
}); |
187 |
$("div.save_quotes").html('<input type="button" class="add_quote_button" value="Save Quotes" style="float: right;" onclick="fnGetData(document.getElementById(\'quotes_editor\'));"/>'); |
188 |
}, |
189 |
}); |
190 |
} |
191 |
|
192 |
function fnHandleFileSelect(evt) { |
193 |
// Reset progress indicator on new file selection. |
194 |
progress.style.width = '0%'; |
195 |
progress.textContent = '0%'; |
196 |
|
197 |
reader = new FileReader(); |
198 |
reader.onerror = fnErrorHandler; |
199 |
reader.onprogress = fnUpdateProgress; |
200 |
reader.onabort = function(e) { |
201 |
alert('File read cancelled'); |
202 |
}; |
203 |
reader.onloadstart = function(e) { |
204 |
document.getElementById('progress_bar').className = 'loading'; |
205 |
}; |
206 |
reader.onload = function(e) { |
207 |
// Ensure that the progress bar displays 100% at the end. |
208 |
progress.style.width = '100%'; |
209 |
progress.textContent = '100%'; |
210 |
setTimeout("document.getElementById('progress_bar').className='';", 2000); |
211 |
quotes = fnCSVToArray(e.target.result, ','); |
212 |
fnDataTable(quotes); |
213 |
} |
214 |
// Read in the image file as a text string. |
215 |
reader.readAsText(evt.target.files[0]); |
216 |
} |
217 |
|
218 |
document.getElementById('files').addEventListener('change', fnHandleFileSelect, false); |
219 |
|
220 |
}); |
221 |
|
222 |
function fnGetData(element) { |
223 |
var jqXHR = $.ajax({ |
224 |
url : "/cgi-bin/koha/tools/quotes/quotes-upload_ajax.pl", |
225 |
type : "POST", |
226 |
contentType : "application/x-www-form-urlencoded", // we must claim this mimetype or CGI will not decode the URL encoding |
227 |
dataType : "json", |
228 |
data : { |
229 |
"quote" : JSON.stringify(oTable.fnGetData()), |
230 |
"action" : "add", |
231 |
}, |
232 |
success : function(){ |
233 |
var response = JSON.parse(jqXHR.responseText); |
234 |
if (response.success) { |
235 |
$("#server_response").text(response.records+' quotes saved.'); |
236 |
} |
237 |
else { |
238 |
$("#server_response").text('An error has occurred. '+response.records+' quotes saved. Please ask your administrator to check the server log for more details.'); |
239 |
} |
240 |
$("#server_response").fadeIn(200); |
241 |
}, |
242 |
}); |
243 |
} |
244 |
|
245 |
function fnClickDeleteRow(td) { |
246 |
oTable.fnDeleteRow(oTable.fnGetPosition(td)[0]); |
247 |
} |
248 |
|
249 |
function fnResetUpload() { |
250 |
$('#server_response').fadeOut(200); |
251 |
window.location.reload(true); // is this the best route? |
252 |
} |
253 |
|
254 |
//]]> |
255 |
</script> |
256 |
</head> |
257 |
<body id="tools_quotes" class="tools"> |
258 |
[% INCLUDE 'header.inc' %] |
259 |
[% INCLUDE 'cat-search.inc' %] |
260 |
|
261 |
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> › Quote Uploader</div> |
262 |
|
263 |
<div id="doc3" class="yui-t2"> |
264 |
<div id="bd"> |
265 |
<div id="yui-main"> |
266 |
<div class="yui-b"> |
267 |
|
268 |
<div id="file_uploader" style="float: left; width: 100%; visibility:visible;"> |
269 |
<input type="file" id="files" name="file" /> |
270 |
<button onclick="fnAbortRead();">Cancel Upload</button> |
271 |
<div id="progress_bar"><div class="percent">0%</div></div> |
272 |
</div> |
273 |
<div id="server_response" onclick='fnResetUpload()'>Server Response</div> |
274 |
<table id="quotes_editor" style="float: left; width: 100%; visibility:hidden;"> |
275 |
<thead> |
276 |
<tr> |
277 |
<th>Source</th> |
278 |
<th>Text</th> |
279 |
<th>Actions</th> |
280 |
</tr> |
281 |
</thead> |
282 |
<tbody> |
283 |
<!-- tbody content is generated by DataTables --> |
284 |
<tr> |
285 |
<td></td> |
286 |
<td>Loading data...</td> |
287 |
<td></td> |
288 |
</tr> |
289 |
</tbody> |
290 |
</table> |
291 |
|
292 |
|
293 |
|
294 |
</div> |
295 |
</div> |
296 |
<div class="yui-b noprint"> |
297 |
[% INCLUDE 'tools-menu.inc' %] |
298 |
</div> |
299 |
</div> |
300 |
[% INCLUDE 'intranet-bottom.inc' %] |