function entry(data){ var id = ko.utils.unwrapObservable(data.id); var value_date = ko.utils.unwrapObservable(data.value_date); var operation_date = ko.utils.unwrapObservable(data.operation_date); var label = ko.utils.unwrapObservable(data.label); var value = ko.utils.unwrapObservable(data.value); var account = ko.utils.unwrapObservable(data.account); var sold = ko.utils.unwrapObservable(data.sold); var pointedSold = ko.utils.unwrapObservable(data.pointedSold); var category = ko.utils.unwrapObservable(data.category); this.id=ko.observable(id ? id : null); this.value_date=ko.observable(value_date ? value_date : null); this.operation_date=ko.observable(operation_date ? operation_date : null); this.label=ko.observable(label ? label : null); this.value=ko.observable(value ? value : null); this.account=ko.observable(account ? account : null); this.sold=ko.observable(sold ? sold : null); this.pointedSold=ko.observable(pointedSold ? pointedSold : null); this.category=ko.observable(category ? category : null); } function message(alertType, title, message) { $(".alert").alert('close'); $("#message-placeholder").append('

' + title + '

' + message + '
'); } var ListViewModel = function() { var self = this; self.account = ko.observable(); self.accounts = ko.observableArray([]); self.entries = ko.observableArray([]); self.months = ko.observableArray(); self.month = ko.observable(); self.selectedItem = ko.observable(); self.savedItem = ko.observable(); self.itemToRemove = ko.observable(); self.chart = null; self.categoriesChart = ko.computed(function() { var entries=ko.utils.unwrapObservable(self.entries); var chartValues = []; var chartValuesTmp = {}; $.each(entries, function(index, entry) { var category = entry.category(); var value = entry.value() ? Number(entry.value()) : null; if(category && value) { var oldValue = 0.0; if(chartValuesTmp[category]) { oldValue = chartValuesTmp[category]; } chartValuesTmp[category] = oldValue - value } }); $.each(chartValuesTmp, function(key, value) { chartValues.push([key, value]); }); return chartValues; }); self.entriesChart = ko.computed(function() { var entries = ko.utils.unwrapObservable(self.entries).slice().reverse(); var chartValues = []; var chartValuesTmp = {}; $.each(entries, function(index, entry) { //var date = entry.value_date() ? entry.value_date().toString() : null; var date = entry.value_date(); var value = entry.value ? Number(entry.value()) : null; if(date && value) { var values = {}; var sold = Number(entry.sold()); var open = Number((sold - value).toFixed(2)); values['open'] = open; values['high'] = sold > open ? sold : open; values['low'] = sold < open ? sold : open; values['close'] = sold; if(chartValuesTmp[date]) { var oldValues = chartValuesTmp[date]; if(oldValues['high'] > values['high']) { values['high'] = oldValues['high']; } if(oldValues['low'] < values['low']) { values['low'] = oldValues['low']; } values['open'] = oldValues['open']; } chartValuesTmp[date] = values; } }); $.each(chartValuesTmp, function(key, value) { var ohlc = [key, value['open'], value['high'], value['low'], value['close']]; chartValues.push(ohlc); }); chartValues.sort(function(a, b){ var aDate = Date.parse(a[0]); var bDate = Date.parse(b[0]); return aDate < bDate ? -1 : 1; }); return chartValues; }, self); self.loadEntries = function(account, month) { $.post("api/entry.php", {action: "get_entries", account: account.id, year: month.year, month: month.month}, function(data) { self.entries.removeAll(); self.selectedItem(null); var entries = []; $.each(data, function(index, element) { entries.push(new entry({ id: element.id, value_date: new Date(element.value_date), operation_date: element.operation_date ? new Date(element.operation_date) : null, label: element.label, value: element.value, account: element.account_id, sold: element.sold, pointedSold: element.operation_date ? element.pointedsold : '', category: element.category })); }); self.entries(entries); }); }; self.loadAccounts = function() { $.post("api/entry.php", {action: "get_accounts"}).success(function (result) { self.accounts(result); if(self.account()) { $.each(self.accounts(), function(index, account) { if(self.account().id == account.id) { self.account(account); } }); } if(!self.account()){ self.account(result[0]); } self.loadMonths(self.account()); }); }; self.loadMonths = function(account){ $.post("api/entry.php", {action: "get_months", account: account.id}).success(function (result) { self.months(result); if(self.month()) { $.each(self.months(), function(index, month) { if(self.month().year == month.year && self.month().month == month.month) { self.month(month); } }); } if(!self.month()) { self.month(result[result.length - 1]); } self.loadEntries(self.account(), self.month()); }); }; self.templateToUse = function (item) { return self.selectedItem() === item ? 'editTmpl' : 'itemsTmpl'; }; self.edit = function(item) { if(self.savedItem() != null) { self.cancel(); } self.savedItem(new entry(item)); self.selectedItem(item); $("#value_date").datepicker({format: "yyyy-mm-dd"}).on('changeDate', function(ev){ self.selectedItem().value_date(new Date(ev.date.valueOf())); }); $("#operation_date").datepicker({format: "yyyy-mm-dd"}).on('changeDate', function(ev){ self.selectedItem().operation_date(new Date(ev.date.valueOf())); }); $("#operation_date_clear").click(function(e){ self.selectedItem().operation_date(null); }); }; self.cancel = function() { if(self.selectedItem() && self.savedItem()) { self.selectedItem().id(self.savedItem().id()); self.selectedItem().operation_date(self.savedItem().operation_date()); self.selectedItem().value_date(self.savedItem().value_date()); self.selectedItem().label(self.savedItem().label()); self.selectedItem().value(self.savedItem().value()); self.selectedItem().account(self.savedItem().account()); } // This item was just added. if(self.selectedItem() && !self.selectedItem().id()) { self.entries.remove(self.selectedItem()); } self.savedItem(null); self.selectedItem(null); }; self.add = function() { if(self.savedItem() != null) { self.cancel(); } var newEntry = new entry({ account: self.account().id }); self.entries.unshift(newEntry); self.selectedItem(newEntry); $("#value_date").datepicker({format: "yyyy-mm-dd"}).on('changeDate', function(ev){ self.selectedItem().value_date(new Date(ev.date.valueOf())); }); $("#operation_date").datepicker({format: "yyyy-mm-dd"}).on('changeDate', function(ev){ self.selectedItem().operation_date(new Date(ev.date.valueOf())); }); $("#operation_date_clear").click(function(e){ self.selectedItem().operation_date(null); }); }; self.save = function() { var item = new entry(self.selectedItem()); if(item.value_date()) { item.value_date(dateToString(item.value_date())); } if(item.operation_date()) { item.operation_date(dateToString(item.operation_date())); } $.post("api/entry.php", {action: "save_entry", entry:item}).success(function(data) { message("success", "Save", data.message); self.selectedItem(null); self.loadAccounts(); }).error(function() { message("error", "Error.", "Unexpected error."); }); }; self.remove = function (item) { if (item.id()) { self.itemToRemove(item); $('#remove-confirm').modal(); } else { self.entries.remove(item); } }; self.confirmRemove = function() { var item = self.itemToRemove(); $.post("api/entry.php", {action: "remove_entry", entry:item}).success(function (result) { self.loadAccounts(); }).complete(function (result) { self.itemToRemove(null); $('#remove-confirm').modal('hide'); }); }; self.selectMonth = function(month) { if(month) { self.month(month); self.loadEntries(self.account(), month); } }; self.selectAccount = function(account) { if(account) { self.account(account); self.loadMonths(account); } }; }; function dateToString(date) { if(date) { var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var dateStr = year + "-" + (month < 10 ? '0' : '') + month + "-" + (day < 10 ? '0' : '') + day; return dateStr;; } else { return ''; } }; ko.bindingHandlers.date = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { var unwrap = ko.utils.unwrapObservable; var dataSource = valueAccessor(); var value = dataSource ? unwrap(dataSource) : null; if(value) { $(element).text(dateToString(value)); } else { $(element).text(''); } }, update: function (element, valueAccessor, allBindingsAccessor, viewModel) { var unwrap = ko.utils.unwrapObservable; var dataSource = valueAccessor(); var value = dataSource ? unwrap(dataSource) : null; if(value) { $(element).text(dateToString(value)); } else { $(element).text(''); } } }; ko.bindingHandlers.dateValue = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { var unwrap = ko.utils.unwrapObservable; var dataSource = valueAccessor(); var value = dataSource ? unwrap(dataSource) : null; if(value) { $(element).val(dateToString(value)); } else { $(element).val(''); } }, update: function (element, valueAccessor, allBindingsAccessor, viewModel) { var unwrap = ko.utils.unwrapObservable; var dataSource = valueAccessor(); var value = dataSource ? unwrap(dataSource) : null; if(value) { $(element).val(dateToString(value)); } else { $(element).val(''); } } }; drawChart = function(entries, element) { // clear previous chart $(element).html(""); if(entries && entries.length > 0) { var day = 24 * 60 * 60 * 1000; var lastDate = new Date(Date.parse(entries[entries.length -1][0]).valueOf() + 1 * day); var firstDate = new Date(Date.parse(entries[0][0]).valueOf() - 1 * day); var chartValues = [[], []]; // For the '0' line. chartValues[0].push( [new Date(firstDate.valueOf()).toString(), 0], [new Date(lastDate.valueOf()).toString(), 0]); chartValues[1] = entries; // plot chart window.chart = $.jqplot(element.id, chartValues, { axes:{ xaxis:{ renderer:$.jqplot.DateAxisRenderer, tickOptions: {formatString: "%F"} }, yaxis: { autoscale: true, } }, highlighter: { show:true, yvalues: 4, formatString:'
date:%s
open:%s
hi:%s
low:%s
close:%s
' }, series: [{ linePattern: "dashed", lineWidth: 1, color: "red", showMarker: false },{ renderer:$.jqplot.OHLCRenderer, color: "blue", lineWidth: 3, rendererOptions:{ }}], }); } else { window.chart = null; } }; drawPieChart = function(entries, element) { // clear previous chart $(element).html(""); if(entries && entries.length > 0) { var chartValues = [[]]; chartValues[0] = entries; // plot chart window.pieChart = $.jqplot(element.id, chartValues, { seriesDefaults: { renderer: $.jqplot.PieRenderer, rendererOptions: { showDataLabels: true } }, legend: { show: true, location: 'e' }, highlighter: { show: true, formatString:'%s: %s', tooltipLocation:'sw', useAxesFormatters:false } }); } else { window.pieChart = null; } }; ko.bindingHandlers.chart = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { // empty - left as placeholder if needed later }, update: function (element, valueAccessor, allBindingsAccessor, viewModel) { var unwrap = ko.utils.unwrapObservable; var dataSource = valueAccessor(); //var entries = dataSource ? unwrap(dataSource) : null; var entries = dataSource ? unwrap(dataSource) : null; drawChart(entries, element); } }; ko.bindingHandlers.pieChart = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { // empty - left as placeholder if needed later }, update: function (element, valueAccessor, allBindingsAccessor, viewModel) { var unwrap = ko.utils.unwrapObservable; var dataSource = valueAccessor(); //var entries = dataSource ? unwrap(dataSource) : null; var entries = dataSource ? unwrap(dataSource) : null; drawPieChart(entries, element); } }; $(document).ajaxError(function(event, xhr, settings) { message("error", "Error.", xhr.statusText); }); $(window).resize(function() { if(window.chart) { window.chart.replot({resetAxes: true}); } if(window.pieChart) { window.pieChart.replot({resetAxes: true}); } }); var viewModel = new ListViewModel(); ko.applyBindings(viewModel); $(viewModel.loadAccounts);