PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/views/timesheet/_timesheet-week-old.php

https://bitbucket.org/newicon/nii-modules-project
PHP | 958 lines | 738 code | 69 blank | 151 comment | 24 complexity | 154f9921950f874406012a42a747c6b3 MD5 | raw file
  1. <?php
  2. /**
  3. * Old timesheet code that displays an airbus (and freshbooks) style week view
  4. *
  5. * @author Newicon, Steven O'Brien <steven.obrien@newicon.net>
  6. * @link http://github.com/newicon/Nii
  7. * @copyright Copyright &copy; 2009-2011 Newicon Ltd
  8. * @license http://newicon.net/framework/license/
  9. */
  10. ?>
  11. <?php Yii::app()->clientScript->registerCoreScript('maskedinput'); ?>
  12. <?php $this->renderPartial('_templates'); ?>
  13. <ul class="nav nav-tabs">
  14. <li class="active"><a href="#timesheet" data-toggle="tab">Week</a></li>
  15. <li >
  16. <a href="#day" data-toggle="tab">Day</a>
  17. </li>
  18. </ul>
  19. <div class="tab-content">
  20. timesheet day
  21. <div class="tab-pane " id="day">
  22. <form class="form-horizontal">
  23. <fieldset>
  24. <legend>Timesheet Day</legend>
  25. <div class="control-group">
  26. <label for="focusedInput" class="control-label">Project</label>
  27. <div class="controls" id="project-select">
  28. </div>
  29. </div>
  30. <div class="control-group">
  31. <label for="focusedInput" class="control-label">Task</label>
  32. <div class="controls">
  33. <input type="text" id="focusedInput" class="input-xlarge focused">
  34. </div>
  35. </div>
  36. <div class="control-group">
  37. <label for="focusedInput" class="control-label">Task</label>
  38. <div class="controls">
  39. <?php $this->widget('nii.widgets.forms.DateInput', array('name'=>'date', 'value'=>date('Y-m-d',time()))); ?>
  40. </div>
  41. </div>
  42. <div class="control-group">
  43. <label for="focusedInput" class="control-label">Time</label>
  44. <div class="controls">
  45. <input type="text" id="time" class="focused" style="width:40px;">
  46. <script>
  47. $(function(){
  48. $.mask.definitions['m']='[012345]';
  49. $('#time').mask('9:?m9',{placeholder:' '});
  50. });
  51. </script>
  52. </div>
  53. </div>
  54. <div class="control-group">
  55. <label for="focusedInput" class="control-label">Notes</label>
  56. <div class="controls">
  57. <textarea class="input-xlarge"></textarea>
  58. </div>
  59. </div>
  60. <div class="form-actions">
  61. <button class="btn btn-primary" type="submit">Save changes</button>
  62. <button class="btn" type="reset">Cancel</button>
  63. </div>
  64. </fieldset>
  65. </form>
  66. </div>
  67. timesheet tab
  68. <div class="tab-pane active" id="timesheet">
  69. <div id="timesheet-selector" class="line well">
  70. <div class="unit size1of3 txtR btn-group">
  71. <a class="btn prev-month"><i class="icon-backward"></i></a><a class="btn prev-week"><i class="icon-chevron-left"></i></a>
  72. </div>
  73. <div class="unit size1of3 txtC">
  74. <span class="date-start">2 January</span> - <span class="date-end">9 January, 2012</span>
  75. </div>
  76. <div class="lastUnit txtL">
  77. <div class="pull-right btn-group">
  78. <a class="btn next-week"><i class="icon-chevron-right"></i></a><a class="btn next-month"><i class="icon-forward"></i></a>
  79. </div>
  80. <a class="btn btn-primary addLog">Add Log</a>
  81. </div>
  82. </div>
  83. <form>
  84. <input type="hidden" name="log[date]" id="log_date" />
  85. <table id="timesheet-grid" class="condensed-table bordered-table zebra-striped table table-bordered table-striped">
  86. <thead>
  87. <tr class="date-headings">
  88. <th class="project-col">Project</th>
  89. <th class="task-col">Task</th>
  90. <th class="hour_units mon-col">Mon<br/><span class="sdate">30 Jan</span></th>
  91. <th class="hour_units tue-col">Tue<br/><span class="sdate">30 Jan</span></th>
  92. <th class="hour_units wed-col">Wed<br/><span class="sdate">30 Jan</span></th>
  93. <th class="hour_units thu-col">Thu<br/><span class="sdate">30 Jan</span></th>
  94. <th class="hour_units fri-col">Fri<br/><span class="sdate">30 Jan</span></th>
  95. <th class="hour_units sat-col">Sat<br/><span class="sdate">30 Jan</span></th>
  96. <th class="hour_units sun-col">Sun<br/><span class="sdate">30 Jan</span></th>
  97. <th class=" total-col">Total</th>
  98. <th class=""></th>
  99. </tr>
  100. </thead>
  101. <tfoot>
  102. <tr id="timesheet-totals">
  103. <th colspan="2"></th>
  104. <th class="hour_units mon-col">0:00</th>
  105. <th class="hour_units tue-col">0:00</th>
  106. <th class="hour_units wed-col">0:00</th>
  107. <th class="hour_units thu-col">0:00</th>
  108. <th class="hour_units fri-col">0:00</th>
  109. <th class="hour_units sat-col">0:00</th>
  110. <th class="hour_units sun-col">0:00</th>
  111. <th class="total">0:00</th>
  112. <th></th>
  113. </tr>
  114. </tfoot>
  115. <tbody>
  116. </tbody>
  117. </table>
  118. </form>
  119. <div class="txtR"><a class="saveLog btn btn-primary btn-large">Save Log</a></div>
  120. </div>
  121. </div>
  122. <script type="text/javascript">
  123. jQuery(function($){
  124. // page is now ready, initialize the calendar...
  125. window.project = {};
  126. window.timesheet = {
  127. // can be set to hours or minutes
  128. format:'time',
  129. // calendar view
  130. cal:{},
  131. // store our Model classes
  132. models:{},
  133. // store our view classes
  134. views:{},
  135. // model storing the start and end dates for timesheet week
  136. timesheet:null,
  137. timeLogList:{},
  138. timeLogRowList:{},
  139. // time log rows parent view
  140. timeLogRows:{},
  141. // stores the project collection
  142. projects:null,
  143. // task collection
  144. tasks:null,
  145. /**
  146. * Init the timesheet app
  147. * start time is the unix epoch time in seconds (equivelent to PHP's)
  148. * note: javascripts unix time is in milliseconds so multiply by 1000
  149. * this function is only called once, to set the date after init use the time model directly
  150. */
  151. init:function(startTime){
  152. this.projects = new this.models.Projects;
  153. this.projects.reset(<?php echo $projects; ?>);
  154. this.tasks = new this.models.Tasks;
  155. this.tasks.reset(<?php echo $tasks; ?>);
  156. // javascript unix epoch is in milliseconds multiple by 1000
  157. var d = new Date(parseInt(startTime)*1000);
  158. this.timesheet = new this.models.Timesheet();
  159. this.cal = new CTimesheetCal({model:this.timesheet});
  160. // start the ball rolling
  161. this.timesheet.set({startDate:d});
  162. this.timeLogList = new this.models.TimeLogCollection;
  163. this.timeLogRowList = new this.models.TimeLogRowCollection;
  164. // var timeLogRowsView = new this.views.TimeLogRows({collection:this.timeLogList});
  165. this.timeLogRows = new this.views.TimeLogRows({collection:this.timeLogRowList});
  166. this.timeLogList.reset(<?php echo $logs; ?>);
  167. $('.saveLog').click(_.bind(function(){
  168. var date = window.timesheet.dateToMysql(window.timesheet.timesheet.get('startDate'));
  169. $('#log_date').val(date);
  170. var data = $('#timesheet form').serializeArray();
  171. data.push({name:'log[format]',value:'time'});
  172. $.post("<?php echo NHtml::url('/timesheet/timesheet/saveWeekLog') ?>", data, function(){
  173. // refresh the timesheet
  174. window.timesheet.tasks.fetch({success:function(){
  175. window.timesheet.timeLogList.refresh();
  176. }});
  177. });
  178. },this));
  179. },
  180. dateToMysql:function(date){
  181. return date.getFullYear() + '-' +
  182. (date.getMonth() < 9 ? '0' : '') + (date.getMonth()+1) + '-' +
  183. (date.getDate() < 10 ? '0' : '') + date.getDate();
  184. },
  185. /**
  186. * converts a mysql date string to a javascript date object
  187. * @param string date mysql date string YYYY-MM-DD
  188. */
  189. mysqlToDate:function(date){
  190. var t = date.split(/[- :]/);
  191. // Apply each element to the Date function
  192. return new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5]);
  193. },
  194. getStartDate:function(){
  195. return this.timesheet.get('startDate');
  196. },
  197. /**
  198. * If the day integer matches the current day returns true
  199. * @param int day number integer 0 = sunday, monday = 1
  200. */
  201. isToday:function(day){
  202. var d = new Date();
  203. return (day == d.getDay());
  204. },
  205. printToday:function(day){
  206. return this.isToday(day) ? 'today' : '';
  207. },
  208. /**
  209. * hours:minutes a time of 1:30 will return 90 minutes
  210. * @param string time format 1:20 (H:MM)
  211. */
  212. timeToMinutes:function(time){
  213. var hm = time.split(':');
  214. var h = parseInt(hm[0]);
  215. var m = parseInt(hm[1]);
  216. if(_.isNaN(h))h = 0;
  217. if(_.isNaN(m))m = 0;
  218. return (h*60) + m;
  219. },
  220. /**
  221. * Takes a number of minutes and returns a time H:MM
  222. *
  223. * @param int minutes total minutes
  224. */
  225. minutesToTime:function(minutes){
  226. var mins = minutes % 60;
  227. var hours = Math.floor(minutes / 60);
  228. // add leading zero on minutes
  229. mins = '0'+mins;
  230. mins = mins.substr(mins.length-2);
  231. return hours + ':' + mins;
  232. }
  233. }
  234. // model to contain the current active week.
  235. // and data about the timesheet
  236. timesheet.models.Timesheet = Backbone.Model.extend({
  237. defaults:{
  238. startDate:null
  239. }
  240. });
  241. // represents the individual database records
  242. timesheet.models.TimeLog = Backbone.Model.extend({
  243. defaults:{
  244. id:null,
  245. date:null,
  246. row:0,
  247. project_id:0,
  248. task_id:0,
  249. minutes:0
  250. },
  251. url : function() {
  252. // Important! It's got to know where to send its REST calls.
  253. // In this case, POST to '/donuts' and PUT to '/donuts/:id'
  254. if(this.id)
  255. return '<?php echo NHtml::url("/timesheet/api/log") ?>/'+this.id;
  256. else
  257. return '<?php echo NHtml::url("/timesheet/api/log") ?>';
  258. },
  259. start:null,
  260. end:null,
  261. /**
  262. * return the javascript date object for the date attribute
  263. */
  264. date:function(){
  265. return window.timesheet.mysqlToDate(this.get('date'));
  266. },
  267. // set the start and end date for this time log
  268. setDate:function(start, end){
  269. this.start = new Date(start.getTime());
  270. this.end = new Date(end.getTime());
  271. var minutes = (end.getTime() - start.getTime()) / 60000;
  272. this.set({
  273. date:$.fullCalendar.formatDate(start, 'yyyy-MM-dd HH:mm:ss'),
  274. minutes:minutes
  275. })
  276. },
  277. startTime:function(){
  278. return $.fullCalendar.formatDate(this.start, 'ddd, d MMMM, HH:mm') ;
  279. },
  280. endTime:function(){
  281. return $.fullCalendar.formatDate(this.end, 'HH:mm')
  282. },
  283. createCalEventObj:function(){
  284. var start = this.date();
  285. var end = new Date(start.getTime() + this.get('minutes')*60000);
  286. var event = {
  287. className:'event-'+this.cid,
  288. id:this.cid,
  289. title:' ',
  290. start:start,
  291. end:end,
  292. allDay: false
  293. };
  294. this.setDate(start, end);
  295. return event;
  296. }
  297. });
  298. timesheet.models.TimeLogCollection = Backbone.Collection.extend({
  299. url:'<?php echo NHtml::url("/timesheet/api/log") ?>',
  300. model:timesheet.models.TimeLog,
  301. getProjects:function(){
  302. return this.groupBy(function(m){
  303. return m.get('project_id') + m.get('task_id');
  304. })
  305. },
  306. fetchLogsFor:function(date){
  307. this.fetch({data: $.param({ date: window.timesheet.dateToMysql(date)}) });
  308. },
  309. fetchLogsForCal:function(startd, endd){
  310. this.fetch({data: $.param({ start: startd}) });
  311. },
  312. // refresh the log list, this will force a redraw of the timesheet table
  313. refresh:function(){
  314. var date = window.timesheet.timesheet.get('startDate');
  315. this.fetchLogsFor(date);
  316. }
  317. })
  318. // represents a custom condensed week log individual timeLogs are condensed into this.
  319. // grouped by project_id
  320. timesheet.models.TimeLogRow = Backbone.Model.extend({
  321. defaults:{
  322. // position within the table
  323. row:0,
  324. editable:false,
  325. id:null,
  326. date:null,
  327. row:0,
  328. project_id:0,
  329. task_id:0,
  330. mon:'',tue:'',wed:'',thu:'',fri:'',sat:'',sun:''
  331. }
  332. });
  333. // stores collection of time log row
  334. timesheet.models.TimeLogRowCollection = Backbone.Collection.extend({
  335. initialize:function(){
  336. // when the timeloglist collection changes we need to rebuild this collection
  337. window.timesheet.timeLogList.bind('reset', this.build, this);
  338. },
  339. model:timesheet.models.TimeLogRow,
  340. build:function(){
  341. // remove all from the collection
  342. var rows = Array();
  343. _.each(timesheet.timeLogList.getProjects(),function(row, i){
  344. // each project has one timeLogRow
  345. rows[rows.length] = new timesheet.models.TimeLogRow({
  346. project_id : row[0].get('project_id'),
  347. task_id : row[0].get('task_id'),
  348. mon : this.sumDayLogs(row, window.timesheet.cal.mon),
  349. tue : this.sumDayLogs(row, window.timesheet.cal.tue),
  350. wed : this.sumDayLogs(row, window.timesheet.cal.wed),
  351. thu : this.sumDayLogs(row, window.timesheet.cal.thu),
  352. fri : this.sumDayLogs(row, window.timesheet.cal.fri),
  353. sat : this.sumDayLogs(row, window.timesheet.cal.sat),
  354. sun : this.sumDayLogs(row, window.timesheet.cal.sun)
  355. });
  356. }, this);
  357. this.reset(rows);
  358. },
  359. /**
  360. * @param collection an array of CTimeLogs for the week grouped by project_id
  361. */
  362. sumDayLogs:function(rowCollection ,date){
  363. var logs = _.filter(rowCollection, function(log){
  364. return log.get('date').substring(0,10) == window.timesheet.dateToMysql(date).substring(0,10);
  365. }, this);
  366. var mins = 0;
  367. if(logs.length > 0){
  368. _.each(logs, function(l){
  369. mins = mins + parseInt(l.get('minutes'));
  370. }, this);
  371. }
  372. if(window.timesheet.format == 'time'){
  373. //mins = mins / 60;
  374. // mins = mins.toFixed(2);
  375. mins = timesheet.minutesToTime(mins);
  376. }
  377. return (mins=='0:00') ? '' : mins;
  378. }
  379. });
  380. timesheet.models.Project = Backbone.Model.extend({
  381. defaults:{
  382. name:''
  383. },
  384. /**
  385. * gets the name and formats it to highlight the last search string
  386. * @return string html
  387. */
  388. getNameSearchHighlight:function(){
  389. return this.getNameHighlight(window.timesheet.projects.nameSearchFilter)
  390. },
  391. /**
  392. * return a string with the filter text highlighted in a strong tag
  393. * @param string filterName
  394. * @return string html
  395. */
  396. getNameHighlight:function(highlight){
  397. return this.get('name').replace(
  398. new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(highlight) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"
  399. );
  400. }
  401. });
  402. /**
  403. * Project Collection
  404. */
  405. timesheet.models.Projects = Backbone.Collection.extend({
  406. model:timesheet.models.Project,
  407. displayProject:function(project_id){
  408. var p = this.get(project_id);
  409. return (_.isNull(p)||_.isUndefined(p)) ? 'unknown' : p.get('link');
  410. },
  411. /**
  412. * store the last name search filter string. as passed to this.filterByNameSearch
  413. */
  414. nameSearchFilter:'',
  415. /**
  416. * function to filter projects by a partial string name match
  417. * @param string nameSearchFilter the search string to look up screens by name
  418. * @return array
  419. */
  420. filterByProjectName:function(nameSearchFilter){
  421. this.nameSearchFilter = nameSearchFilter;
  422. var matcher = new RegExp($.ui.autocomplete.escapeRegex(nameSearchFilter), "i");
  423. var filtered = this.filter(function(model) {
  424. return (model.get('id') && (!nameSearchFilter || matcher.test(model.get('name'))));
  425. });
  426. return filtered;
  427. }
  428. });
  429. timesheet.models.Task = Backbone.Model.extend({
  430. defaults:{name:''},
  431. /**
  432. * gets the name and formats it to highlight the last search string
  433. * @return string html
  434. */
  435. getNameSearchHighlight:function(){
  436. return this.getNameHighlight(window.timesheet.tasks.nameSearchFilter)
  437. },
  438. /**
  439. * return a string with the filter text highlighted in a strong tag
  440. * @param string filterName
  441. * @return string html
  442. */
  443. getNameHighlight:function(highlight){
  444. return this.get('name').replace(
  445. new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(highlight) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"
  446. );
  447. }
  448. });
  449. /**
  450. * Task Collection
  451. */
  452. timesheet.models.Tasks = Backbone.Collection.extend({
  453. model:timesheet.models.Task,
  454. url:"<?php echo NHtml::url('/timesheet/timesheet/tasks'); ?>",
  455. nameSearchFilter:null,
  456. displayTask:function(task_id){
  457. var t = this.get(task_id);
  458. return _.isEmpty(t) ? 'unknown' : t.get('name');
  459. },
  460. filterTaskForProject:function(id, nameSearchFilter){
  461. this.nameSearchFilter = nameSearchFilter;
  462. var filtered = this.filter(function(model){
  463. return model.get('project_id') == id
  464. });
  465. return filtered;
  466. console.log(filtered)
  467. this.nameSearchFilter = nameSearchFilter;
  468. var matcher = new RegExp($.ui.autocomplete.escapeRegex(nameSearchFilter), "i");
  469. var filtered = _.each(filtered,function(model) {
  470. return (model.get('id') && (!nameSearchFilter || matcher.test(model.get('name'))));
  471. });
  472. return filtered;
  473. }
  474. });
  475. /**
  476. * parent view to create table rows
  477. * each row is a timesheet.models.TimeLogRow model
  478. * (1 model per row and per project and task id combination)
  479. */
  480. timesheet.views.TimeLogRows = Backbone.View.extend({
  481. el:$('#timesheet-grid tbody'),
  482. initialize:function(){
  483. this.collection.bind('reset', this.render, this);
  484. this.collection.bind('add', this.addOne, this);
  485. this.collection.bind('remove', this.doTotals, this);
  486. },
  487. events:{},
  488. render:function(){
  489. this.$el.html('');
  490. this.collection.forEach(function(row){
  491. this.addOne(row)
  492. }, this)
  493. // update totals
  494. $(':input').unbind('timesheet_totals');
  495. $('#timesheet-grid').delegate(':input','blur change.timesheet_totals',_.bind(this.doTotals,this));
  496. this.doTotals();
  497. },
  498. doTotals:function(){
  499. // cols first
  500. this.doTotal($('#timesheet-grid tbody td.mon-col'), $('#timesheet-totals .mon-col'));
  501. this.doTotal($('#timesheet-grid tbody td.tue-col'), $('#timesheet-totals .tue-col'));
  502. this.doTotal($('#timesheet-grid tbody td.wed-col'), $('#timesheet-totals .wed-col'));
  503. this.doTotal($('#timesheet-grid tbody td.thu-col'), $('#timesheet-totals .thu-col'));
  504. this.doTotal($('#timesheet-grid tbody td.fri-col'), $('#timesheet-totals .fri-col'));
  505. this.doTotal($('#timesheet-grid tbody td.sat-col'), $('#timesheet-totals .sat-col'));
  506. this.doTotal($('#timesheet-grid tbody td.sun-col'), $('#timesheet-totals .sun-col'));
  507. // update rows
  508. _.each($('#timesheet-grid tbody tr'),function(e){
  509. this.doTotal($(e).find('td.field'), $(e).find('.total-col'));
  510. }, this);
  511. // update main total
  512. this.doTotal($('#timesheet-totals th.hour_units'), $('#timesheet-totals th.total'));
  513. },
  514. // count totals and update elements
  515. doTotal:function(el, elTotal){
  516. var total=0;
  517. el.each(function(){
  518. if($(this).is('input')){
  519. var val = $(this).val();
  520. }else{
  521. var val = $(this).text();
  522. $input = $(this).find('input');
  523. if($input.length)
  524. val = $input.val();
  525. }
  526. // get the total in minutes
  527. var num = timesheet.timeToMinutes(val);
  528. total = total + num;
  529. });
  530. elTotal.html(timesheet.minutesToTime(total));
  531. },
  532. addRowForm:function(){
  533. var log = new window.timesheet.models.TimeLogRow({
  534. date:window.timesheet.dateToMysql(window.timesheet.getStartDate()),
  535. editable:true
  536. });
  537. this.collection.add(log);
  538. },
  539. addOne:function(model){
  540. // update the models row position
  541. model.set({row:$('#timesheet-grid tbody tr').length});
  542. var row = new window.timesheet.views.TimeLogRow({model:model});
  543. this.$el.append(row.render().el)
  544. row.focusProject();
  545. this.doTotals();
  546. }
  547. });
  548. /**
  549. * view for each unique row project_id and task_id
  550. */
  551. timesheet.views.TimeLogRow = Backbone.View.extend({
  552. tagName:'tr',
  553. template:_.template($('#time-log-row-template').html()),
  554. initialize:function(){
  555. this.model.bind('destroy', this.remove, this);
  556. this.model.bind('change:project_id', this.projectIdChange, this);
  557. window.timesheet.projects.bind('add', this.redrawProjects, this);
  558. },
  559. events:{
  560. 'click .record-delete':'deleteRow',
  561. 'click .project .down':'projectAutocompleteDropDown',
  562. 'change input.time':'timeChange'
  563. },
  564. render:function(){
  565. this.$el.html(this.template(this.model.toJSON()));
  566. if(this.model.get('editable')){
  567. //add input mask to time fields
  568. $.mask.definitions['m']='[012345]';
  569. this.$('input.time').mask('9:?m9',{placeholder:' '});
  570. this.projectAutocomplete();
  571. // this.taskAutocomplete();
  572. this.$('.task .input').addClass('disabled');
  573. this.$('.task input').attr('disabled','disabled');
  574. }
  575. return this;
  576. },
  577. // update the time format after user input
  578. // mainly add trailing zeros if minutes have not been entered
  579. timeChange:function(e){
  580. if(timesheet.format == 'time'){
  581. var time = $(e.target).val()+'00';
  582. $(e.target).val(time.substr(0,4));
  583. }
  584. },
  585. focusProject:function(){
  586. this.$('.project .ui-autocomplete-input').focus();
  587. },
  588. projectAutocomplete:function(){
  589. this.$('.project input.select').autocomplete('destroy');
  590. this.$('.project input.select').autocomplete({
  591. minLength: 0,
  592. source: function(request, response) {
  593. response(window.timesheet.projects.filterByProjectName(request.term));
  594. },
  595. select:_.bind(function(event, ui) {
  596. this.model.set({'project_id':ui.item.get('id')});
  597. //this.model.save();
  598. return false;
  599. },this),
  600. change:_.bind(function(event, ui){
  601. if(_.isNull(ui.item)) {
  602. // remove invalid value, as it didn't match anything
  603. this.$('.project input.select').val("");
  604. this.$('.project input.project-id').val("");
  605. this.$('.project input.select').data("autocomplete").term = "";
  606. return false;
  607. }
  608. },this),
  609. position:{'my':'left top','at':'left bottom','of':this.$('.project input'),'collision':'flip'}
  610. })
  611. .data("autocomplete")._renderItem = _.bind(function(ul, item) {
  612. return $("<li></li>")
  613. .data("item.autocomplete", item)
  614. .append("<a>" + item.getNameSearchHighlight() + "</a>")
  615. .appendTo(ul);
  616. },this);
  617. },
  618. // function called when the drop down button of the combo box is clicked
  619. projectAutocompleteDropDown:function(){
  620. // close if already visible
  621. if (this.$('.project input.select').autocomplete("widget").is(":visible")) {
  622. this.$('.project input.select').autocomplete("close");
  623. return false;
  624. }
  625. // work around a bug (likely same cause as #5265)
  626. $(this).blur();
  627. // pass empty string as value to search for, displaying all results
  628. this.$('.project input.select').autocomplete("search", "");
  629. this.$('.project input.select').focus();
  630. return false;
  631. },
  632. // update the view with new project id
  633. projectIdChange:function(){
  634. var p = window.timesheet.projects.get(this.model.get('project_id'));
  635. this.$('.project input.select').val(p.get('name'));
  636. this.$('.project input.project-id').val(p.get('id'));
  637. // trigger reload of task list
  638. //this.$('.task input').addClass('loading');
  639. this.taskAutocomplete();
  640. },
  641. taskAutocomplete:function(){
  642. this.$('.task input').removeAttr('disabled').focus();
  643. this.$('.task .input').removeClass('disabled');
  644. this.$('.task input').autocomplete('destroy');
  645. this.$('.task input').autocomplete({
  646. minLength: 0,
  647. source: _.bind(function(request, response) {
  648. response(window.timesheet.tasks.filterTaskForProject(this.model.get('project_id'), request.term));
  649. },this),
  650. select:_.bind(function(event, ui) {
  651. //this.model.set({'task_id':ui.item.get('id')});
  652. //alert(ui.item.get('name'))
  653. this.$('.task input').val(ui.item.get('name'));
  654. },this),
  655. // change:_.bind(function(event, ui){
  656. // if(_.isNull(ui.item)) {
  657. // // invalid value?
  658. // }
  659. // },this),
  660. position:{'my':'left top','at':'left bottom','of':this.$('.task input'),'collision':'flip'}
  661. })
  662. .data("autocomplete")._renderItem = _.bind(function(ul, item) {
  663. return $("<li></li>")
  664. .data("item.autocomplete", item)
  665. .append("<a>" + item.getNameSearchHighlight() + "</a>")
  666. .appendTo(ul);
  667. },this);
  668. },
  669. deleteRow:function(){
  670. this.model.destroy();
  671. }
  672. })
  673. // timesheet
  674. var CTimesheetCal = Backbone.View.extend({
  675. months:null,
  676. msWeek:604800000,
  677. msDay:86400000,
  678. mon:null,tue:null,wed:null,thu:null,fri:null,sat:null,sun:null,
  679. events:{
  680. 'click .prev-week':'prevWeek',
  681. 'click .next-week':'nextWeek',
  682. 'click .prev-month':'prevMonth',
  683. 'click .next-month':'nextMonth',
  684. 'click .addLog':'addLog'
  685. },
  686. initialize:function(){
  687. this.setElement($('#timesheet-selector'));
  688. this.months = Array();
  689. this.months[this.months.length] = 'January';
  690. this.months[this.months.length] = 'February';
  691. this.months[this.months.length] = 'March';
  692. this.months[this.months.length] = 'April';
  693. this.months[this.months.length] = 'May';
  694. this.months[this.months.length] = 'June';
  695. this.months[this.months.length] = 'July';
  696. this.months[this.months.length] = 'August';
  697. this.months[this.months.length] = 'September';
  698. this.months[this.months.length] = 'October';
  699. this.months[this.months.length] = 'November';
  700. this.months[this.months.length] = 'December';
  701. this.model.bind('change:startDate', this.render, this);
  702. },
  703. render:function(){
  704. var time = this.model.get('startDate').getTime();
  705. this.mon = this.model.get('startDate');
  706. this.tue = new Date(time+this.msDay);
  707. this.wed = new Date(time+this.msDay*2);
  708. this.thu = new Date(time+this.msDay*3);
  709. this.fri = new Date(time+this.msDay*4);
  710. this.sat = new Date(time+this.msDay*5);
  711. this.sun = new Date(time+(this.msDay*6));
  712. this.$('.date-start').html(this.mon.getDate()+' '+this.month(this.mon));
  713. this.$('.date-end').html(this.sun.getDate()+' '+this.month(this.sun) + ', ' + this.sun.getFullYear());
  714. // update displayed timesheet table dates
  715. $('.date-headings .mon-col .sdate').html(this.mon.getDate()+' '+this.monthShort(this.mon));
  716. $('.date-headings .tue-col .sdate').html(this.tue.getDate()+' '+this.monthShort(this.tue));
  717. $('.date-headings .wed-col .sdate').html(this.wed.getDate()+' '+this.monthShort(this.wed));
  718. $('.date-headings .thu-col .sdate').html(this.thu.getDate()+' '+this.monthShort(this.thu));
  719. $('.date-headings .fri-col .sdate').html(this.fri.getDate()+' '+this.monthShort(this.fri));
  720. $('.date-headings .sat-col .sdate').html(this.sat.getDate()+' '+this.monthShort(this.sat));
  721. $('.date-headings .sun-col .sdate').html(this.sun.getDate()+' '+this.monthShort(this.sun));
  722. },
  723. month:function(d){
  724. return this.months[d.getMonth()];
  725. },
  726. monthShort:function(d){
  727. return this.month(d).substring(0,3);
  728. },
  729. getStartTime:function(){
  730. return this.model.get('startDate').getTime();
  731. },
  732. setStartTime:function(miliseconds, operator){
  733. if(operator=='-')
  734. var start = new Date(this.getStartTime() - miliseconds);
  735. else
  736. var start = new Date(this.getStartTime() + miliseconds);
  737. this.model.set({startDate:start});
  738. window.timesheet.timeLogList.fetchLogsFor(start);
  739. },
  740. prevWeek:function(){
  741. this.setStartTime(this.msWeek, '-');
  742. },
  743. nextWeek:function(){
  744. this.setStartTime(this.msWeek, '+');
  745. },
  746. nextMonth:function(){
  747. this.setStartTime(this.msWeek * 4, '+');
  748. },
  749. prevMonth:function(){
  750. this.setStartTime(this.msWeek * 4, '-');
  751. },
  752. addLog:function(){
  753. window.timesheet.timeLogRows.addRowForm();
  754. }
  755. });
  756. // GO GO GO!
  757. window.timesheet.init(<?php echo $startDate; ?>);
  758. //window.timesheet.timeLogList = new timesheet.models.TimeLogCollection();
  759. // full calendar js:
  760. var CProjectView = Backbone.View.extend({
  761. className:'inputContainer input-xlarge',
  762. render:function(){
  763. var p = window.timesheet.projects.get(this.model.get('project_id'));
  764. var val = (_.isUndefined(p)||_.isNull(p)) ? '' : p.get('name');
  765. this.$el.append('<div class="input-xlarge" style="position:relative;">'
  766. + '<input class="select input-xlarge" type="text" value="'+val+'" />'
  767. + '<span style="position:absolute;top:5px;right:0px;cursor:pointer;" class="down sprite fam-bullet-arrow-down"></span></div>');
  768. this.autocomplete();
  769. return this;
  770. },
  771. events:{
  772. 'click .down':'dropDown'
  773. },
  774. autocomplete:function(){
  775. this.$('.select').autocomplete('destroy');
  776. this.$('.select').autocomplete({
  777. minLength: 0,
  778. source: function(request, response) {
  779. response(window.timesheet.projects.filterByProjectName(request.term));
  780. },
  781. select:_.bind(function(event, ui) {
  782. this.$('.select').val(ui.item.get('name'));
  783. this.$('.project-id').val(ui.item.get('id'));
  784. this.model.set({'project_id':ui.item.get('id')});
  785. //this.model.save();
  786. return false;
  787. },this),
  788. change:_.bind(function(event, ui){
  789. if(_.isNull(ui.item)) {
  790. // remove invalid value, as it didn't match anything
  791. this.$('.select').val("");
  792. this.$('.project-id').val("");
  793. this.$('.select').data("autocomplete").term = "";
  794. return false;
  795. }
  796. },this),
  797. position:{'my':'left top','at':'left bottom','of':this.$('.select'),'collision':'flip'}
  798. })
  799. .data("autocomplete")._renderItem = _.bind(function(ul, item) {
  800. return $("<li></li>")
  801. .data("item.autocomplete", item)
  802. .append("<a>" + item.getNameSearchHighlight() + "</a>")
  803. .appendTo(ul);
  804. },this);
  805. },
  806. // function called when the drop down button of the combo box is clicked
  807. dropDown:function(){
  808. // close if already visible
  809. if (this.$('.select').autocomplete("widget").is(":visible")) {
  810. this.$('.select').autocomplete("close");
  811. return false;
  812. }
  813. // work around a bug (likely same cause as #5265)
  814. $(this).blur();
  815. // pass empty string as value to search for, displaying all results
  816. this.$('.select').autocomplete("search", "");
  817. this.$('.select').focus();
  818. return false;
  819. }
  820. });
  821. var CLogForm = Backbone.View.extend({
  822. template:_.template($('#timelog-pop-template').html()),
  823. initialize:function(){
  824. // this.setElement($('#timelog-pop'));
  825. //this.model.on('change', this.render, this);
  826. this.model.on('destroy', this.remove, this);
  827. this.model.on('change:project_id', this.loadJobs, this);
  828. this.render();
  829. },
  830. events:{
  831. 'click .save':'save',
  832. 'click .cancel':'cancel',
  833. 'click .delete':'deleteLog'
  834. },
  835. render:function(){
  836. this.$el.css({
  837. display:'none',position:'relative',width:'400px',backgroundColor:'#fff',boxShadow:'0px 0px 5px #000',zIndex:3
  838. });
  839. $('#timelog-pop').html(this.$el.html(this.template({log:this.model})));
  840. var p = new CProjectView({model:this.model});
  841. p.setElement(this.$('.project'));
  842. p.render();
  843. var eventEl = $('.event-'+this.model.cid);
  844. if(eventEl.length==0)
  845. eventEl = $('.fc-select-helper');
  846. this.$el.show().position({my:'center bottom',at:'center top', of:eventEl, offset:'0px -15px'})
  847. return this;
  848. },
  849. loadJobs:function(){
  850. $.get("<?php echo NHtml::url('/timesheet/timesheet/jobs') ?>", {projectId:this.model.get('project_id')}, _.bind(function(r){
  851. this.$('.job').html(r);
  852. },this));
  853. },
  854. save:function(e){
  855. this.model.save();
  856. // calendar.fullCalendar('refetchEvents');
  857. var ev = this.model.createCalEventObj();
  858. console.log(ev);
  859. this.close();
  860. },
  861. cancel:function(e){
  862. calendar.fullCalendar('refetchEvents');
  863. this.close();
  864. return false;
  865. },
  866. deleteLog:function(e){
  867. window.calendar.fullCalendar('removeEvents', [this.model.cid]);
  868. this.model.destroy();
  869. return false;
  870. },
  871. close:function(){
  872. this.remove();
  873. this.model.off();
  874. }
  875. })
  876. var CCalView = Backbone.View.extend({
  877. initialize:function(){
  878. window.timesheet.timeLogList.on('remove', this.removeLog, this);
  879. },
  880. removeLog:function(log){
  881. window.calendar.fullCalendar('removeEvents', [log.cid]);
  882. }
  883. });
  884. window.calView = new CCalView();
  885. window.curForm = null;
  886. //var p = new CProjectView;
  887. //$('#project-select').html(p.render().el);
  888. });
  889. </script>