condo_interface 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,5 +1,11 @@
1
- h1. condo_interface
1
+ h1. Condo Interface
2
2
 
3
3
  The default UI for the Condo project. Feel free to use, extend, adapt or write your own.
4
+ Condo Interface is under the terms of the MIT License.
4
5
 
5
6
 
7
+ h2. Usage
8
+
9
+ "Condo":https://github.com/cotag/Condominios is built as an "AngularJS application":http://angularjs.org/ which implements "models":http://docs.angularjs.org/guide/concepts#model and a "controller":http://docs.angularjs.org/guide/concepts#controller for a "Rails backend":http://rubyonrails.org/
10
+ Condo Interface is an example "view":http://docs.angularjs.org/guide/concepts#view with an associated "directive":http://docs.angularjs.org/guide/concepts#directives that interface with the Condo controller.
11
+
@@ -20,10 +20,10 @@
20
20
  (function (factory) {
21
21
  if (typeof define === 'function' && define.amd) {
22
22
  // AMD
23
- define(['jquery', 'condo_uploader'], factory);
23
+ define(['jquery', 'condo_controller'], factory);
24
24
  } else {
25
25
  // Browser globals
26
- factory(jQuery, window.CondoUploader);
26
+ factory(jQuery, window.CondoController);
27
27
  }
28
28
  }(function ($, uploads, undefined) {
29
29
  'use strict';
@@ -43,7 +43,10 @@
43
43
  // Allow for both mobile and desktop events or both
44
44
  // Overkill?
45
45
  //
46
- uploads.directive('coTap', function() {
46
+ var condoInterface = angular.module('CondoInterface', ['CondoUploader'])
47
+
48
+
49
+ condoInterface.directive('coTap', function() {
47
50
 
48
51
 
49
52
  //
@@ -145,94 +148,238 @@
145
148
  //
146
149
  // create a directive for attaching the input events
147
150
  //
148
- uploads.directive('coUploads', ['Condo.Broadcast', function(broadcast) {
149
- return function(scope, element, attrs) {
150
- var options = {
151
- delegate: attrs['coDelegate'] || element,
152
- drop_targets: attrs['coTargets'] || element,
153
- hover_class: attrs['coHoverClass'] || 'drag-hover',
154
- pre_check: attrs['coAccepts'] || '/./i',
155
- size_limit: attrs['coLimit'] || 0
156
- };
157
-
158
-
159
- if(!!attrs['coEndpoint'])
160
- scope.endpoint = attrs['coEndpoint'];
151
+ condoInterface.directive('coUploads', ['Condo.Broadcast', '$timeout', function(broadcast, $timeout) {
152
+ return {
153
+ controller: 'Condo.Controller',
154
+ link: function(scope, element, attrs) {
155
+ var options = {
156
+ delegate: attrs['coDelegate'] || element,
157
+ drop_targets: attrs['coTargets'] || element,
158
+ hover_class: attrs['coHoverClass'] || 'drag-hover',
159
+ pre_check: attrs['coAccepts'] || '/./i',
160
+ size_limit: attrs['coLimit'] || 0
161
+ },
162
+
163
+ //
164
+ // Add files with their path information to the system
165
+ // Queue items here until we decide they should be added to the view
166
+ //
167
+ processPending = function() {
168
+ var avaliable = view_limit - scope.upload_count;
169
+
170
+ if(avaliable > 0 && pending_items.length > 0) {
171
+
172
+ var item = pending_items.shift(),
173
+ items = item.items,
174
+ length = items.length;
175
+
176
+ if(item.folders) {
177
+ var i = 0,
178
+ entry,
179
+ obj,
180
+ count = 0,
181
+ new_items = [],
182
+ processEntry = function(entry, path) {
183
+ //
184
+ // If it is a directory we add it to the pending queue
185
+ //
186
+ try {
187
+ if (entry.isDirectory) {
188
+ entry.createReader().readEntries(function(entries) {
189
+
190
+ pending_items.push({
191
+ items: entries,
192
+ folders: true,
193
+ path: path + entry.name + '/'
194
+ });
195
+ checkCount();
196
+ });
197
+ } else if (entry.isFile) { // Files are added to a file queue
198
+ entry.file(function(file) {
199
+ if(path.length > 0)
200
+ file.dir_path = path;
201
+
202
+ new_items.push(file);
203
+
204
+ checkCount();
205
+ });
206
+ } else {
207
+ checkCount();
208
+ }
209
+ } catch(err) {
210
+ //
211
+ // TODO:: hmmmm
212
+ //
213
+ checkCount();
214
+ }
215
+
216
+ },
217
+ checkCount = function() {
218
+ //
219
+ // Counts the entries processed so we can add any files to the queue
220
+ //
221
+ count += 1;
222
+ if (count >= length) {
223
+ if(new_items.length > 0) {
224
+ pending_items.unshift({ // add any files to the start of the queue
225
+ items: new_items,
226
+ folders: false
227
+ });
228
+ }
229
+ safeApply(scope, function() {
230
+ $timeout(processPending);
231
+ });
232
+ }
233
+ };
234
+
235
+ for (; i < length; i++) {
236
+
237
+ //
238
+ // first layer of DnD folders require you to getAsEntry
239
+ //
240
+ if(item.path.length == 0) {
241
+ obj = items[i];
242
+ obj.getAsEntry = obj.getAsEntry || obj.webkitGetAsEntry || obj.mozGetAsEntry;
243
+ entry = obj.getAsEntry();
244
+ } else {
245
+ entry = items[i];
246
+ }
247
+ processEntry(entry, item.path);
248
+ }
249
+ } else if(length <= avaliable) { // Regular files where we can add them all at once
250
+ scope.add(items);
251
+ $timeout(processPending); // Delay until next tick (delay and invoke apply are optional)
252
+ } else { // Regular file where we can't add them all at once
253
+ scope.add(items.splice(0, avaliable));
254
+ pending_items.unshift(item);
255
+ }
256
+ }
257
+ },
258
+ view_limit = 50, // Number of uploads that should be displayed at once
259
+ pending_items = []; // These are files or folders that have not been processed yet as we are at the view port limit
161
260
 
162
261
 
163
- scope.options = options;
164
-
165
-
166
- //
167
- // Determine how to draw the element
168
- //
169
- if(document.implementation.hasFeature("org.w3c.svg", "1.0")) {
170
- element.addClass('supports-svg');
171
- } else {
172
- element.addClass('no-svg');
173
- }
262
+ if(!!attrs['coEndpoint'])
263
+ scope.endpoint = attrs['coEndpoint'];
264
+
174
265
 
266
+ scope.options = options;
267
+ scope.remove_completed = false; // Remove completed uploads automatically
175
268
 
176
- //
177
- // Detect file drops
178
- //
179
- options.drop_targets = $(options.drop_targets);
180
- options.delegate = $(options.delegate).on('drop.condo', options.drop_targets, function(event) {
181
- options.drop_targets.removeClass(options.hover_class);
182
269
 
183
270
  //
184
- // Prevent propagation early (so any errors don't cause unwanted behaviour)
271
+ // Determine how to draw the element
185
272
  //
186
- event.preventDefault();
187
- event.stopPropagation();
273
+ if(document.implementation.hasFeature("org.w3c.svg", "1.0")) {
274
+ element.addClass('supports-svg');
275
+ } else {
276
+ element.addClass('no-svg');
277
+ }
278
+
279
+
280
+ //
281
+ // Detect file drops
282
+ //
283
+ options.drop_targets = $(options.drop_targets);
284
+ options.delegate = $(options.delegate).on('drop.condo', options.drop_targets, function(event) {
285
+ options.drop_targets.removeClass(options.hover_class);
286
+
287
+ //
288
+ // Prevent propagation early (so any errors don't cause unwanted behaviour)
289
+ //
290
+ event.preventDefault();
291
+ event.stopPropagation();
292
+
293
+
294
+ if (!!event.originalEvent.dataTransfer.items) {
295
+ pending_items.push({
296
+ items: event.originalEvent.dataTransfer.items,
297
+ folders: true,
298
+ path: ''
299
+ });
300
+ } else if(!!event.originalEvent.dataTransfer.files) {
301
+ var files = event.originalEvent.dataTransfer.files,
302
+ copy = [],
303
+ i = 0;
304
+
305
+ for (; i < files.length; i += 1)
306
+ copy.push(files[i]);
307
+
308
+ pending_items.push({
309
+ items: copy,
310
+ folders: false
311
+ });
312
+ }
313
+
314
+ safeApply(scope, function() {
315
+ processPending();
316
+ });
317
+ }).on('dragover.condo', options.drop_targets, function(event) {
318
+ $(this).addClass(options.hover_class);
319
+
320
+ return false;
321
+ }).on('dragleave.condo', options.drop_targets, function(event) {
322
+ $(this).removeClass(options.hover_class);
323
+
324
+ return false;
325
+ }).
326
+
188
327
 
189
- safeApply(scope, function() {
190
- scope.add(event.originalEvent.dataTransfer.files);
328
+ //
329
+ // Detect manual file uploads
330
+ //
331
+ on('change.condo', ':file', function(event) {
332
+ var files = $(this)[0].files,
333
+ copy = [],
334
+ i = 0;
335
+
336
+ for (; i < files.length; i += 1)
337
+ copy.push(files[i]);
338
+
339
+ $(this).parents('form')[0].reset();
340
+
341
+ pending_items.push({
342
+ items: copy,
343
+ folders: false
344
+ });
345
+
346
+ safeApply(scope, function() {
347
+ processPending();
348
+ });
191
349
  });
192
- }).on('dragover.condo', options.drop_targets, function(event) {
193
- $(this).addClass(options.hover_class);
194
350
 
195
- return false;
196
- }).on('dragleave.condo', options.drop_targets, function(event) {
197
- $(this).removeClass(options.hover_class);
198
351
 
199
- return false;
200
- }).
201
-
202
-
203
- //
204
- // Detect manual file uploads
205
- //
206
- on('change.condo', ':file', function(event) {
207
- var self = $(this);
208
- safeApply(scope, function() {
209
- scope.add(self[0].files);
210
- self.parents('form')[0].reset();
352
+ //
353
+ // Add new uploads if possible
354
+ //
355
+ scope.$watch('upload_count', function(newValue, oldValue) {
356
+ processPending();
211
357
  });
212
- });
213
-
214
-
215
- //
216
- // Clean up any event handlers
217
- //
218
- scope.$on('$destroy', function() {
219
- options.drop_targets.off('.condo');
220
- options.delegate.off('.condo');
221
- element.removeClass('supports-svg').removeClass('no-svg');
222
- });
223
-
224
-
225
- scope.$on('coFileAddFailed', function() {
226
- alert('Failed to add file: ' + broadcast.message.reason);
227
- });
228
-
229
-
230
- scope.humanReadableByteCount = function(bytes, si) {
231
- var unit = si ? 1000.0 : 1024.0;
232
- if (bytes < unit) return bytes + (si ? ' iB' : ' B');
233
- var exp = Math.floor(Math.log(bytes) / Math.log(unit)),
234
- pre = (si ? 'kMGTPE' : 'KMGTPE').charAt(exp-1) + (si ? 'iB' : 'B');
235
- return (bytes / Math.pow(unit, exp)).toFixed(1) + ' ' + pre;
358
+
359
+
360
+ //
361
+ // Clean up any event handlers
362
+ //
363
+ scope.$on('$destroy', function() {
364
+ options.drop_targets.off('.condo');
365
+ options.delegate.off('.condo');
366
+ element.removeClass('supports-svg').removeClass('no-svg');
367
+ });
368
+
369
+
370
+ scope.$on('coFileAddFailed', function() {
371
+ // TODO:: need an unobtrusive notification system for failed adds
372
+ // alert('Failed to add file: ' + broadcast.message.reason);
373
+ });
374
+
375
+
376
+ scope.humanReadableByteCount = function(bytes, si) {
377
+ var unit = si ? 1000.0 : 1024.0;
378
+ if (bytes < unit) return bytes + (si ? ' iB' : ' B');
379
+ var exp = Math.floor(Math.log(bytes) / Math.log(unit)),
380
+ pre = (si ? 'kMGTPE' : 'KMGTPE').charAt(exp-1) + (si ? 'iB' : 'B');
381
+ return (bytes / Math.pow(unit, exp)).toFixed(1) + ' ' + pre;
382
+ }
236
383
  }
237
384
  }
238
385
  }]);
@@ -242,7 +389,7 @@
242
389
  // The individual upload events
243
390
  // Triggers the pause, resume, abort functions
244
391
  //
245
- uploads.directive('coUpload', function() {
392
+ condoInterface.directive('coUpload', function() {
246
393
  var PENDING = 0,
247
394
  STARTED = 1,
248
395
  PAUSED = 2,
@@ -274,7 +421,10 @@
274
421
  element.find('td.controls').replaceWith( '<td class="blank" />' );
275
422
  element.find('div.bar').removeClass('animate');
276
423
 
277
- scope.check_autostart();
424
+ if(scope.remove_completed)
425
+ scope.animate_remove();
426
+ else
427
+ scope.check_autostart(); // Couldn't work out how to put this into the controller
278
428
  break;
279
429
 
280
430
  case PAUSED:
@@ -284,6 +434,9 @@
284
434
 
285
435
  scope.paused = true;
286
436
  // No need for break
437
+
438
+ if (scope.ignore_errors && scope.upload.error)
439
+ scope.check_autostart(); // Couldn't work out how to put this into the controller
287
440
  }
288
441
  });
289
442
 
@@ -305,4 +458,62 @@
305
458
  };
306
459
  });
307
460
 
461
+
462
+ //
463
+ // Toggling options
464
+ // based on: https://github.com/angular-ui/bootstrap/tree/master/src/dropdownToggle
465
+ //
466
+ condoInterface.directive('dropdownToggle', ['$document', '$location', '$window', function ($document, $location, $window) {
467
+ var openElement = null, close;
468
+ return {
469
+ restrict: 'CA',
470
+ link: function(scope, element, attrs) {
471
+ scope.$watch(function dropdownTogglePathWatch() {return $location.path();}, function dropdownTogglePathWatchAction() {
472
+ if (close) { close(); }
473
+ });
474
+
475
+ element.parent().bind('click', function(event) {
476
+ event.stopPropagation();
477
+ });
478
+
479
+ element.bind('click', function(event) {
480
+ event.preventDefault();
481
+ event.stopPropagation();
482
+
483
+ var iWasOpen = false;
484
+
485
+ if (openElement) {
486
+ iWasOpen = openElement === element;
487
+ close();
488
+ }
489
+
490
+ if (!iWasOpen){
491
+ element.parent().addClass('open');
492
+ openElement = element;
493
+
494
+ close = function (event) {
495
+ if (event) {
496
+ event.preventDefault();
497
+ event.stopPropagation();
498
+ }
499
+ $document.unbind('click', close);
500
+ element.parent().removeClass('open');
501
+ close = null;
502
+ openElement = null;
503
+ };
504
+
505
+ $document.bind('click', close);
506
+ }
507
+ });
508
+
509
+
510
+ //
511
+ // Center the pop-up, based on CSS location of the button
512
+ //
513
+ var popup = element.next('ul.dropdown-menu');
514
+ popup.css('margin-left', -(popup.width() / 2) + 'px');
515
+ }
516
+ };
517
+ }]);
518
+
308
519
  }));
@@ -9,6 +9,7 @@
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
11
  *= require_self
12
+ *= require condo_interface/bootstrap/condo_custom
12
13
  *= require condo_interface/uploads
13
14
  *= require condo_interface/images
14
15
  *= require_tree .