Changeset 9335
- Timestamp:
- 03/27/13 16:34:48 (12 years ago)
- Location:
- branches/OaaS/HeuristicLab.Services.Optimization.Web
- Files:
-
- 2 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.controller.js
r9324 r9335 190 190 // selectableGroup = new OAAS_VIEW.SelectableGroup({ collection: availableExperiments, el: $('#experiments') }); 191 191 selectableGroup.render(); 192 draggableExperiments.render(); 192 193 193 194 wizardController.listenTo(selectableGroup, 'node-selected', function (nodeId) { -
branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.view.js
r9324 r9335 197 197 var self = this; 198 198 this.$el.smartWizard({ 199 keyNavigation: false, 199 200 onLeaveStep: function (step) { 200 201 var stepNum = parseInt(step.attr("rel")); … … 286 287 newWizard.appendTo(this.$el); 287 288 newWizard.smartWizard({ 289 keyNavigation: false, 288 290 onFinish: function () { 289 291 self.trigger('parameters-finished'); … … 704 706 }); 705 707 706 707 my.PlotView = Backbone.View.extend({708 render: function () {709 this.$el.empty();710 var plotLabels = undefined;711 if (this.model.RowNames) {712 plotLabels = [];713 for (var i = 0; i < this.model.RowNames.length; i++) {714 plotLabels.push({ label: this.model.RowNames[i] });715 }716 }717 718 var values = this.model.Value ? this.model.Value : this.model;719 720 var plotValues = [];721 if ($.isArray(values[0])) {722 var columnCount = values[0].length;723 var colVals = {};724 for (var i = 0; i < columnCount; i++)725 colVals[i] = [];726 727 for (var i = 0; i < values.length; i++) {728 for (var j = 0; j < columnCount; j++) {729 colVals[j].push(values[i][j]);730 }731 }732 733 for (var i = 0; i < columnCount; i++) {734 plotValues.push(colVals[i]);735 }736 } else {737 plotValues.push(values);738 }739 740 if (!this.$el.attr('id')) {741 this.$el.attr('id', my.PlotView.nextId());742 }743 744 this.$el.css('width', '500px');745 746 this.plot = $.jqplot(this.$el.attr('id'), plotValues, {747 cursor: {748 show: true,749 zoom: true,750 showTooltip: false751 },752 series: plotLabels,753 seriesDefaults: {754 lineWidth: 1.5,755 markerOptions: {756 size: 2,757 lineWidth: 2758 }759 },760 legend: {761 show: true,762 placement: 'outsideGrid'763 },764 highlighter: {765 show: true,766 sizeAdjust: 7.5767 }768 });769 },770 refresh: function () {771 if (this.plot)772 this.plot.replot({ resetAxes: true });773 }774 },775 {776 plotId: 1,777 nextId: function () {778 return my.PlotView.plotId++;779 }780 });781 782 708 my.VariationContentView = Backbone.View.extend({ 783 709 render: function () { … … 870 796 } 871 797 }); 798 799 // ========================== Run Collection Views =================================== 800 801 function percentile(p, data) { 802 var i = (data.length * p) / 100 + 0.5; 803 var f = i - parseInt(i); 804 var k = Math.floor(i) - 1; 805 return (1 - f) * data[k] + f * data[k + 1]; 806 } 807 808 my.RunCollectionView = Backbone.View.extend({ 809 events: { 810 'change .x': 'xAxisSelected', 811 'change .y': 'yAxisSelected', 812 'change .plotType': 'plotSelected', 813 'change .datatableOptions': 'datatableSelected', 814 'change .datatableRow': 'datatableRowSelected', 815 'change .bubbleSize': 'bubbleTypeSelected' 816 }, 817 render: function () { 818 var self = this; 819 var ele = $(_.template($('#runcollection_template').html(), {})); 820 var xAxisSelect = $('.x', ele); 821 var yAxisSelect = $('.y', ele); 822 var results = this.model.models[0].get('results'); 823 this.options.datatables = {}; 824 var datatableSelect = $('.datatableOptions', ele); 825 826 var names = _.reduce(this.model.models, function (arr, model) { 827 return _.union(arr, _.map(model.get('results'), function (elem) { return elem.Name })); 828 }, []); 829 830 this.bubbleSelect = $('select[class="bubbleSize"]', ele); 831 832 _.each(names, function (name) { 833 var option = $("<option />", { value: name, text: name }); 834 option.clone().appendTo(xAxisSelect); 835 option.clone().appendTo(yAxisSelect); 836 option.clone().appendTo(self.bubbleSelect); 837 var rowNames = null; 838 var foundModel = _.find(self.model.models, function (model) { 839 var entry = _.find(model.get('results'), function (entry) { return entry.Name == name && entry.RowNames }); 840 if (entry) 841 rowNames = entry.RowNames; 842 return entry && entry.RowNames; 843 }); 844 if (rowNames) { 845 self.options.datatables[name] = rowNames; 846 $('<option />', { value: name, text: name }).appendTo(datatableSelect); 847 } 848 }); 849 850 // prepare slider settings 851 this.options.bubbleSize = 10; 852 this.options.xAxis = this.model.models[0].get('results')[0].Name; 853 this.options.yAxis = this.options.xAxis; 854 this.options.plot = 'Boxplot'; 855 $('div[class="bubbleSlider"]', ele).slider({ 856 range: "max", 857 min: 10, 858 max: 40, 859 value: 10, 860 stop: function (event, ui) { 861 self.options.bubbleSize = ui.value; 862 self.createPlot(); 863 } 864 }).css('width', '120px').css('margin-left', '15px').css('margin-right', '15px').css('display', 'inline-block'); 865 ele.appendTo(this.$el); 866 867 // init resizer 868 this.plotWidth = 500; 869 this.plotHeight = 500; 870 var parent = $('div[class="resizer"]', this.$el); 871 this.resizerView = new my.ResizableView({ el: parent }); 872 this.listenTo(this.resizerView, 'resized', function (evt) { 873 self.updatePlot( 874 $(evt.target).width() * 0.96, 875 $(evt.target).height() * 0.96 876 ); 877 }); 878 this.resizerView.render(); 879 this.updateRowSelect(datatableSelect.val()); 880 this.createPlot(); 881 this.updatePlot( 882 600, 883 500 884 ); 885 }, 886 updateRowSelect: function (name) { 887 if (!name) 888 return; 889 var rowSelect = $('select[class="datatableRow"]', this.$el); 890 rowSelect.empty(); 891 var rownames = this.options.datatables[name]; 892 for (var i = 0; i < rownames.length; i++) { 893 $('<option />', { value: rownames[i], text: rownames[i] }).appendTo(rowSelect); 894 } 895 this.options.selectedDatatable = name; 896 this.options.datatableRow = rowSelect.val(); 897 this.createPlot(); 898 }, 899 datatableSelected: function (evt) { 900 var value = $(evt.target).val(); 901 this.options.selectedDatatable = value; 902 this.updateRowSelect(value); 903 }, 904 datatableRowSelected: function (evt) { 905 var value = $(evt.target).val(); 906 this.options.datatableRow = value; 907 this.createPlot(); 908 }, 909 xAxisSelected: function (evt) { 910 var target = $(evt.target); 911 this.options.xAxis = target.val(); 912 this.createPlot(); 913 }, 914 yAxisSelected: function (evt) { 915 var target = $(evt.target); 916 this.options.yAxis = target.val(); 917 this.createPlot(); 918 }, 919 plotSelected: function (evt) { 920 var target = $(evt.target); 921 this.options.plot = target.val(); 922 this.createPlot(); 923 }, 924 bubbleTypeSelected: function (evt) { 925 this.createPlot(); 926 }, 927 updateVisibility: function (evt) { 928 if (this.options.plot == 'Boxplot') { 929 $('span[class="choice"]', this.$el).show(); 930 $('span[class="bubble"]', this.$el).hide(); 931 $('span[class="datatable"]', this.$el).hide(); 932 } else if (this.options.plot == 'Bubblechart') { 933 $('span[class="choice"]', this.$el).show(); 934 $('span[class="bubble"]', this.$el).show(); 935 $('span[class="datatable"]', this.$el).hide(); 936 } else if (this.options.plot == 'Datatable') { 937 $('span[class="choice"]', this.$el).hide(); 938 $('span[class="bubble"]', this.$el).hide(); 939 $('span[class="datatable"]', this.$el).show(); 940 } 941 }, 942 updatePlot: function (width, height) { 943 this.plotDiv.height(height); 944 this.plotDiv.width(width); 945 this.plotWidth = width; 946 this.plotHeight = height; 947 this.plot.refresh(); 948 }, 949 createPlot: function () { 950 if (this.options.plot && this.options.xAxis && this.options.yAxis) { 951 if (this.options.plot == 'Boxplot') { 952 this.createBoxplot(); 953 } else if (this.options.plot == 'Bubblechart') { 954 this.createBubblechart(); 955 } else if (this.options.plot == 'Datatable') { 956 this.createDatatable(); 957 } 958 this.updateVisibility(); 959 } 960 }, 961 preparePlotDiv: function () { 962 var parent = $('div[class="plot"]', this.$el); 963 parent.empty(); 964 this.plotDiv = $('<div></div>').css('width', this.plotWidth).css('height', this.plotHeight).appendTo(parent); 965 return this.plotDiv; 966 }, 967 createBoxplot: function () { 968 var self = this; 969 970 // prepare data for boxplot 971 var values = {}; 972 for (var i = 0; i < this.model.models.length; i++) { 973 var xlabel = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.xAxis }); 974 if (!xlabel) 975 continue; 976 977 if ($.isArray(xlabel.Value)) 978 values[xlabel.Name + ' = ' + xlabel.Value[0]] = []; 979 else 980 values[xlabel.Name + ' = ' + xlabel.Value] = []; 981 } 982 983 for (var i = 0; i < this.model.models.length; i++) { 984 var xlabel = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.xAxis }); 985 var entry = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.yAxis }); 986 if (!xlabel || !entry || !entry.Value) 987 continue; 988 989 var index = xlabel.Name + ' = ' + ($.isArray(xlabel.Value) ? xlabel.Value[0] : xlabel.Value); 990 991 if ($.isArray(entry.Value)) 992 values[index].push(entry.Value[0]); 993 else 994 values[index].push(entry.Value); 995 } 996 997 // create boxplot 998 var div = this.preparePlotDiv(); 999 var boxPlot = new my.BoxplotView({ model: values, el: div }); 1000 this.plot = boxPlot; 1001 boxPlot.render(); 1002 }, 1003 createBubblechart: function () { 1004 var self = this; 1005 1006 // prepare data for bubble chart 1007 var values = []; 1008 var autoscaleBubbles = this.bubbleSelect.val() != 'Constant'; 1009 for (var i = 0; i < this.model.models.length; i++) { 1010 var xValue = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.xAxis }); 1011 var yValue = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.yAxis }); 1012 if (!xValue || !yValue) 1013 continue; 1014 1015 // determine size 1016 var size = 0; 1017 if (this.bubbleSelect.val() == 'Constant') { 1018 size = this.options.bubbleSize; 1019 } else { 1020 var valueProvider = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.bubbleSelect.val(); }); 1021 if (!valueProvider) { 1022 size = this.options.bubbleSize; 1023 } else { 1024 var value = valueProvider.Value; 1025 while ($.isArray(value)) { 1026 value = value[0]; 1027 } 1028 size = parseInt(value.replace(/,/, '.')); 1029 if (isNaN(size)) 1030 size = this.options.bubbleSize; 1031 } 1032 } 1033 1034 // add element 1035 var xFloat = parseFloat(xValue.Value.replace(/,/, '.')); 1036 var yFloat = parseFloat(yValue.Value.replace(/,/, '.')); 1037 1038 values.push([xFloat, yFloat, size, 'Run' + (i + 1)]); 1039 } 1040 1041 // render bubble chart 1042 var div = this.preparePlotDiv(); 1043 var bubblePlot = new my.BubbleplotView({ model: values, el: div, autoscale: autoscaleBubbles }); 1044 this.plot = bubblePlot; 1045 bubblePlot.render(); 1046 }, 1047 createDatatable: function () { 1048 if (!this.options.selectedDatatable) 1049 return; 1050 var self = this; 1051 1052 // prepare data for datatable 1053 var values = []; 1054 var rowNames = []; 1055 for (var i = 0; i < this.model.models.length; i++) { 1056 var table = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.selectedDatatable }); 1057 if (!table) 1058 continue; 1059 var index = table.RowNames.indexOf(this.options.datatableRow); 1060 if (index == -1) 1061 continue; 1062 var entries = []; 1063 for (var j = 0; j < table.Value.length; j++) { 1064 entries.push(table.Value[j][index]); 1065 } 1066 values.push(entries); 1067 rowNames.push(this.options.datatableRow + ' of Run Number ' + (i + 1)); 1068 } 1069 1070 // render datatable 1071 var div = this.preparePlotDiv(); 1072 var plot = new my.PlotView({ model: values, el: div, rownames: rowNames }); 1073 this.plot = plot; 1074 plot.render(); 1075 } 1076 }); 1077 1078 // ===================== Plots ============================= 1079 1080 my.PlotView = Backbone.View.extend({ 1081 render: function () { 1082 this.$el.empty(); 1083 var plotLabels = undefined; 1084 var labelSource = this.model.RowNames ? this.model.RowNames : this.options.rownames; 1085 if (labelSource) { 1086 plotLabels = []; 1087 for (var i = 0; i < labelSource.length; i++) { 1088 plotLabels.push({ label: labelSource[i] }); 1089 } 1090 } 1091 1092 var values = this.model.Value ? this.model.Value : this.model; 1093 1094 var plotValues = []; 1095 1096 if (!this.options.transpose) { 1097 if ($.isArray(values[0])) { 1098 for (var i = 0; i < values.length; i++) { 1099 plotValues.push(values[i]); 1100 } 1101 } else { 1102 plotValues.push(values); 1103 } 1104 } 1105 else { 1106 if ($.isArray(values[0])) { 1107 var columnCount = values[0].length; 1108 var colVals = {}; 1109 for (var i = 0; i < columnCount; i++) 1110 colVals[i] = []; 1111 1112 for (var i = 0; i < values.length; i++) { 1113 for (var j = 0; j < columnCount; j++) { 1114 colVals[j].push(values[i][j]); 1115 } 1116 } 1117 1118 for (var i = 0; i < columnCount; i++) { 1119 plotValues.push(colVals[i]); 1120 } 1121 } else { 1122 plotValues.push(values); 1123 } 1124 } 1125 1126 if (!this.$el.attr('id')) { 1127 this.$el.attr('id', my.PlotView.nextId()); 1128 } 1129 1130 if (!this.$el.css('width')) 1131 this.$el.css('width', '500px'); 1132 1133 this.plot = $.jqplot(this.$el.attr('id'), plotValues, { 1134 cursor: { 1135 show: true, 1136 zoom: true, 1137 showTooltip: false 1138 }, 1139 series: plotLabels, 1140 seriesDefaults: { 1141 lineWidth: 1.5, 1142 markerOptions: { 1143 size: 2, 1144 lineWidth: 2 1145 } 1146 }, 1147 legend: { 1148 show: true, 1149 placement: 'outsideGrid' 1150 }, 1151 highlighter: { 1152 show: true, 1153 sizeAdjust: 7.5 1154 } 1155 }); 1156 }, 1157 refresh: function () { 1158 if (this.plot) 1159 this.plot.replot({ resetAxes: true }); 1160 } 1161 }, 1162 { 1163 plotId: 1, 1164 nextId: function () { 1165 return my.PlotView.plotId++; 1166 } 1167 }); 1168 1169 my.BubbleplotView = Backbone.View.extend({ 1170 render: function () { 1171 this.$el.empty(); 1172 1173 var values = this.model && this.model.Value ? this.model.Value : this.model; 1174 1175 var plotValues = []; 1176 1177 1178 if (!this.$el.attr('id')) { 1179 this.$el.attr('id', my.PlotView.nextId()); 1180 } 1181 1182 if (!this.$el.css('width')) 1183 this.$el.css('width', '500px'); 1184 1185 this.plot = $.jqplot(this.$el.attr('id'), [values], { 1186 cursor: { 1187 show: true, 1188 zoom: true, 1189 showTooltip: false 1190 }, 1191 seriesDefaults: { renderer: $.jqplot.BubbleRenderer, 1192 rendererOptions: { 1193 autoscaleBubbles: this.options.autoscale == true, 1194 bubbleGradients: true 1195 }, 1196 shadow: true 1197 } 1198 }); 1199 }, 1200 refresh: function () { 1201 if (this.plot) 1202 this.plot.replot({ resetAxes: true }); 1203 } 1204 }); 1205 1206 my.BoxplotView = Backbone.View.extend({ 1207 render: function () { 1208 this.$el.empty(); 1209 var plotLabels = undefined; 1210 if (this.model && this.model.RowNames) { 1211 plotLabels = []; 1212 for (var i = 0; i < this.model.RowNames.length; i++) { 1213 plotLabels.push({ label: this.model.RowNames[i] }); 1214 } 1215 } 1216 1217 var values = this.model && this.model.Value ? this.model.Value : this.model; 1218 var globalMin = Number.MAX_VALUE; 1219 var globalMax = Number.MIN_VALUE; 1220 1221 var plotValues = []; 1222 1223 for (var key in values) { 1224 var entry = values[key]; 1225 if ($.isArray(entry[0])) { 1226 for (var i = 0; i < entry.length; i++) { 1227 var cnt = entry[i].length; 1228 var mean = _.mean(entry[i]); 1229 var median = _.median(entry[i]); 1230 var min = _.min(entry[i]); 1231 var max = _.max(entry[i]); 1232 var sorted = _.sortBy(entry[i], function (num) { return num; }); 1233 var q1 = percentile(25, sorted); 1234 var q3 = percentile(75, sorted); 1235 var diff = 1.5 * (q3 - q1); 1236 1237 plotValues.push([key, min, q1, median, q3, max]); 1238 //plotValues.push(["Sample " + i, percentile(15, sorted), q1, median, q3, percentile(85, sorted)]); 1239 if (max > globalMax) globalMax = max; 1240 if (min < globalMin) globalMin = min; 1241 } 1242 } else { 1243 var cnt = entry.length; 1244 var mean = _.mean(entry); 1245 var median = _.median(entry); 1246 var min = _.min(entry); 1247 var max = _.max(entry); 1248 var sorted = _.sortBy(entry, function (num) { return num; }); 1249 var q1 = percentile(25, sorted); 1250 var q3 = percentile(75, sorted); 1251 plotValues.push([key, min, q1, median, q3, max]); 1252 if (max > globalMax) globalMax = max; 1253 if (min < globalMin) globalMin = min; 1254 } 1255 } 1256 1257 1258 1259 if (!this.$el.attr('id')) { 1260 this.$el.attr('id', my.PlotView.nextId()); 1261 } 1262 1263 if (!this.$el.css('width')) 1264 this.$el.css('width', '500px'); 1265 1266 this.plot = $.jqplot(this.$el.attr('id'), [plotValues], { 1267 cursor: { 1268 show: true, 1269 zoom: true, 1270 showTooltip: false 1271 }, 1272 series: [{ renderer: $.jqplot.BoxplotRenderer, rendererOptions: {}}], 1273 axesDefaults: {}, 1274 axes: { 1275 yaxis: { 1276 min: globalMin * 0.9, 1277 max: globalMax * 1.1 1278 }, 1279 xaxis: { 1280 renderer: $.jqplot.CategoryAxisRenderer 1281 } 1282 }, 1283 legend: { 1284 show: true, 1285 placement: 'outsideGrid' 1286 }, 1287 highlighter: { 1288 show: true, 1289 sizeAdjust: 7.5 1290 } 1291 }); 1292 }, 1293 refresh: function () { 1294 if (this.plot) 1295 this.plot.replot({ resetAxes: true }); 1296 } 1297 }); 872 1298 return my; 873 1299 } (OAAS_VIEW || {}, Backbone, _, $, OAAS_MODEL)); -
branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/job.view.js
r9324 r9335 83 83 my.ResultView = Backbone.View.extend({ 84 84 render: function () { 85 this.template = _.template($('#runTemplate').html()); 86 this.$el.empty(); 85 this.$el.empty(); 86 var div = $('<h3/>').text('Compare with charts').appendTo(this.$el); 87 var div = $('<div/>').appendTo(this.$el); 88 var rcv = new my.RunCollectionView({model: this.model, el: div}); 89 rcv.render(); 87 90 for (var i = 0; i < this.model.models.length; i++) { 88 91 var rev = new my.ResultEntryView({ model: this.model.models[i], el: this.$el }); … … 157 160 var row = $('<tr/>').attr('data-index', i); 158 161 $('<td/>').text(results[i].Name).appendTo(row); 159 //TODO: Switch Value type and create different items here160 162 if ($.isArray(results[i].Value)) { 161 163 var col = $('<td/>').appendTo(row); 162 164 var tevDiv = $('<div/>'); 163 // TODO: REMOVE TABLEEDITVIEW and create it only on demand!!! 164 // var tev = new my.TableEditView({ model: results[i].Value, el: tevDiv, useDatatable: true }); 165 // tev.render(); 166 var plotDiv = $('<div/>').appendTo($('#tmp')); 167 //var pv = new PlotView({model: results[i], el: plotDiv}); 168 //pv.render(); 165 var plotDiv = $('<div/>').appendTo($('#tmp'));; 169 166 this.plotted[i] = {}; 170 167 var tv = new my.TabView({ … … 190 187 this.plotted[index].Plot = true; 191 188 var plotDiv = $('<div/>').appendTo($('#tmp')); 192 var pv = new my.PlotView({ model: this.model.get('results')[index], el: plotDiv });189 var pv = new my.PlotView({ model: this.model.get('results')[index], el: plotDiv, transpose: true }); 193 190 pv.render(); 194 191 plotDiv.appendTo(evt.element); -
branches/OaaS/HeuristicLab.Services.Optimization.Web/HeuristicLab.Services.Optimization.Web.csproj
r9324 r9335 292 292 <Content Include="Content\ui-lightness\jquery-ui-1.9.2.css" /> 293 293 <Content Include="Content\ui-lightness\jquery-ui-1.9.2.min.css" /> 294 <Content Include="Content\backbone\underscoreAddon.js" /> 294 295 <Content Include="Global.asax" /> 295 296 <Content Include="ControllerService.svc" /> -
branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Job/Index.cshtml
r9324 r9335 74 74 </script> 75 75 76 <script type="text/template" id="runcollection_template"> 77 <div> 78 <span>Plot: 79 <select class="plotType"> 80 <option>Boxplot</option> 81 <option>Bubblechart</option> 82 <option>Datatable</option> 83 </select> 84 </span> 85 <span class="choice"> 86 <span>X: <select class="x"></select></span> 87 <span>Y: <select class="y"></select></span> 88 </span> 89 90 <span class="bubble"> 91 Bubble Size: <select class="bubbleSize"><option>Constant</option></select> <div class="bubbleSlider"></div> 92 </span> 93 94 <span class="datatable"> 95 DataTable: <select class="datatableOptions"></select> 96 DataRow: <select class="datatableRow"></select> 97 </span> 98 <div class="resizer" style="width: 600px"> 99 <div class="plot" id="tmp"> 100 </div> 101 </div> 102 </div> 103 </script> 104 76 105 77 106 <script type='text/javascript'> -
branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Shared/_Layout.cshtml
r9324 r9335 25 25 <script type="text/javascript" src="@Url.Content("~/Content/jqplot/src/plugins/jqplot.pieRenderer.js")"></script> 26 26 <script type="text/javascript" src="@Url.Content("~/Content/jqplot/src/plugins/jqplot.donutRenderer.js")"></script> 27 <script type="text/javascript" src="@Url.Content("~/Content/jqplot/src/plugins/jqplot.bubbleRenderer.js")"></script> 28 <script type="text/javascript" src="@Url.Content("~/Content/jqplot/src/plugins/jqplot.boxplotRenderer.js")"></script> 27 29 <script type="text/javascript" src="@Url.Content("~/Content/jqplot/src/plugins/jqplot.cursor.js")"></script> 28 30 … … 37 39 38 40 <script type='text/javascript' src='@Url.Content("~/Content/backbone/underscore.js")'></script> 41 <script type='text/javascript' src='@Url.Content("~/Content/backbone/underscoreAddon.js")'></script> 39 42 <script type='text/javascript' src='@Url.Content("~/Content/backbone/backbone-min.js")'></script> 40 43 <script type='text/javascript' src='@Url.Content("~/Content/backbone/backbone-relational.js")'></script>
Note: See TracChangeset
for help on using the changeset viewer.