.=< { Star Gans Tq } >=.

  • Home

  • Killme
  • Download
  • Current Path : /home/m/e/h/meharicl/www/recensement2/js/modules/
    Upload File
    @Command ~ $  
    Current File : /home/m/e/h/meharicl/www/recensement2/js/modules/data.src.js

    /**
     * @license Data plugin for Highcharts
     *
     * (c) 2012-2013 Torstein Hønsi
     * Last revision 2013-06-07
     *
     * License: www.highcharts.com/license
     */
    
    /*
     * The Highcharts Data plugin is a utility to ease parsing of input sources like
     * CSV, HTML tables or grid views into basic configuration options for use 
     * directly in the Highcharts constructor.
     *
     * Demo: http://jsfiddle.net/highcharts/SnLFj/
     *
     * --- OPTIONS ---
     *
     * - columns : Array<Array<Mixed>>
     * A two-dimensional array representing the input data on tabular form. This input can
     * be used when the data is already parsed, for example from a grid view component.
     * Each cell can be a string or number. If not switchRowsAndColumns is set, the columns
     * are interpreted as series. See also the rows option.
     *
     * - complete : Function(chartOptions)
     * The callback that is evaluated when the data is finished loading, optionally from an 
     * external source, and parsed. The first argument passed is a finished chart options
     * object, containing series and an xAxis with categories if applicable. Thise options
     * can be extended with additional options and passed directly to the chart constructor.
     *
     * - csv : String
     * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn
     * and endColumn to delimit what part of the table is used. The lineDelimiter and 
     * itemDelimiter options define the CSV delimiter formats.
     * 
     * - endColumn : Integer
     * In tabular input data, the first row (indexed by 0) to use. Defaults to the last 
     * column containing data.
     *
     * - endRow : Integer
     * In tabular input data, the last row (indexed by 0) to use. Defaults to the last row
     * containing data.
     *
     * - googleSpreadsheetKey : String 
     * A Google Spreadsheet key. See https://developers.google.com/gdata/samples/spreadsheet_sample
     * for general information on GS.
     *
     * - googleSpreadsheetWorksheet : String 
     * The Google Spreadsheet worksheet. The available id's can be read from 
     * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic
     *
     * - itemDelimiter : String
     * Item or cell delimiter for parsing CSV. Defaults to ",".
     *
     * - lineDelimiter : String
     * Line delimiter for parsing CSV. Defaults to "\n".
     *
     * - parsed : Function
     * A callback function to access the parsed columns, the two-dimentional input data
     * array directly, before they are interpreted into series data and categories.
     *
     * - parseDate : Function
     * A callback function to parse string representations of dates into JavaScript timestamps.
     * Return an integer on success.
     *
     * - rows : Array<Array<Mixed>>
     * The same as the columns input option, but defining rows intead of columns.
     *
     * - startColumn : Integer
     * In tabular input data, the first column (indexed by 0) to use. 
     *
     * - startRow : Integer
     * In tabular input data, the first row (indexed by 0) to use.
     *
     * - table : String|HTMLElement
     * A HTML table or the id of such to be parsed as input data. Related options ara startRow,
     * endRow, startColumn and endColumn to delimit what part of the table is used.
     */
    
    // JSLint options:
    /*global jQuery */
    
    (function (Highcharts) {	
    	
    	// Utilities
    	var each = Highcharts.each;
    	
    	
    	// The Data constructor
    	var Data = function (dataOptions, chartOptions) {
    		this.init(dataOptions, chartOptions);
    	};
    	
    	// Set the prototype properties
    	Highcharts.extend(Data.prototype, {
    		
    	/**
    	 * Initialize the Data object with the given options
    	 */
    	init: function (options, chartOptions) {
    		this.options = options;
    		this.chartOptions = chartOptions;
    		this.columns = options.columns || this.rowsToColumns(options.rows) || [];
    
    		// No need to parse or interpret anything
    		if (this.columns.length) {
    			this.dataFound();
    
    		// Parse and interpret
    		} else {
    
    			// Parse a CSV string if options.csv is given
    			this.parseCSV();
    			
    			// Parse a HTML table if options.table is given
    			this.parseTable();
    
    			// Parse a Google Spreadsheet 
    			this.parseGoogleSpreadsheet();	
    		}
    
    	},
    
    	/**
    	 * Get the column distribution. For example, a line series takes a single column for 
    	 * Y values. A range series takes two columns for low and high values respectively,
    	 * and an OHLC series takes four columns.
    	 */
    	getColumnDistribution: function () {
    		var chartOptions = this.chartOptions,
    			getValueCount = function (type) {
    				return (Highcharts.seriesTypes[type || 'line'].prototype.pointArrayMap || [0]).length;
    			},
    			globalType = chartOptions && chartOptions.chart && chartOptions.chart.type,
    			individualCounts = [];
    
    		each((chartOptions && chartOptions.series) || [], function (series) {
    			individualCounts.push(getValueCount(series.type || globalType));
    		});
    
    		this.valueCount = {
    			global: getValueCount(globalType),
    			individual: individualCounts
    		};
    	},
    
    
    	dataFound: function () {
    		// Interpret the values into right types
    		this.parseTypes();
    		
    		// Use first row for series names?
    		this.findHeaderRow();
    		
    		// Handle columns if a handleColumns callback is given
    		this.parsed();
    		
    		// Complete if a complete callback is given
    		this.complete();
    		
    	},
    	
    	/**
    	 * Parse a CSV input string
    	 */
    	parseCSV: function () {
    		var self = this,
    			options = this.options,
    			csv = options.csv,
    			columns = this.columns,
    			startRow = options.startRow || 0,
    			endRow = options.endRow || Number.MAX_VALUE,
    			startColumn = options.startColumn || 0,
    			endColumn = options.endColumn || Number.MAX_VALUE,
    			lines,
    			activeRowNo = 0;
    			
    		if (csv) {
    			
    			lines = csv
    				.replace(/\r\n/g, "\n") // Unix
    				.replace(/\r/g, "\n") // Mac
    				.split(options.lineDelimiter || "\n");
    			
    			each(lines, function (line, rowNo) {
    				var trimmed = self.trim(line),
    					isComment = trimmed.indexOf('#') === 0,
    					isBlank = trimmed === '',
    					items;
    				
    				if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) {
    					items = line.split(options.itemDelimiter || ',');
    					each(items, function (item, colNo) {
    						if (colNo >= startColumn && colNo <= endColumn) {
    							if (!columns[colNo - startColumn]) {
    								columns[colNo - startColumn] = [];					
    							}
    							
    							columns[colNo - startColumn][activeRowNo] = item;
    						}
    					});
    					activeRowNo += 1;
    				}
    			});
    
    			this.dataFound();
    		}
    	},
    	
    	/**
    	 * Parse a HTML table
    	 */
    	parseTable: function () {
    		var options = this.options,
    			table = options.table,
    			columns = this.columns,
    			startRow = options.startRow || 0,
    			endRow = options.endRow || Number.MAX_VALUE,
    			startColumn = options.startColumn || 0,
    			endColumn = options.endColumn || Number.MAX_VALUE,
    			colNo;
    			
    		if (table) {
    			
    			if (typeof table === 'string') {
    				table = document.getElementById(table);
    			}
    			
    			each(table.getElementsByTagName('tr'), function (tr, rowNo) {
    				colNo = 0; 
    				if (rowNo >= startRow && rowNo <= endRow) {
    					each(tr.childNodes, function (item) {
    						if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) {
    							if (!columns[colNo]) {
    								columns[colNo] = [];					
    							}
    							columns[colNo][rowNo - startRow] = item.innerHTML;
    							
    							colNo += 1;
    						}
    					});
    				}
    			});
    
    			this.dataFound(); // continue
    		}
    	},
    
    	/**
    	 * TODO: 
    	 * - switchRowsAndColumns
    	 */
    	parseGoogleSpreadsheet: function () {
    		var self = this,
    			options = this.options,
    			googleSpreadsheetKey = options.googleSpreadsheetKey,
    			columns = this.columns,
    			startRow = options.startRow || 0,
    			endRow = options.endRow || Number.MAX_VALUE,
    			startColumn = options.startColumn || 0,
    			endColumn = options.endColumn || Number.MAX_VALUE,
    			gr, // google row
    			gc; // google column
    
    		if (googleSpreadsheetKey) {
    			jQuery.getJSON('https://spreadsheets.google.com/feeds/cells/' + 
    				  googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') +
    					  '/public/values?alt=json-in-script&callback=?',
    					  function (json) {
    					
    				// Prepare the data from the spreadsheat
    				var cells = json.feed.entry,
    					cell,
    					cellCount = cells.length,
    					colCount = 0,
    					rowCount = 0,
    					i;
    			
    				// First, find the total number of columns and rows that 
    				// are actually filled with data
    				for (i = 0; i < cellCount; i++) {
    					cell = cells[i];
    					colCount = Math.max(colCount, cell.gs$cell.col);
    					rowCount = Math.max(rowCount, cell.gs$cell.row);			
    				}
    			
    				// Set up arrays containing the column data
    				for (i = 0; i < colCount; i++) {
    					if (i >= startColumn && i <= endColumn) {
    						// Create new columns with the length of either end-start or rowCount
    						columns[i - startColumn] = [];
    
    						// Setting the length to avoid jslint warning
    						columns[i - startColumn].length = Math.min(rowCount, endRow - startRow);
    					}
    				}
    				
    				// Loop over the cells and assign the value to the right
    				// place in the column arrays
    				for (i = 0; i < cellCount; i++) {
    					cell = cells[i];
    					gr = cell.gs$cell.row - 1; // rows start at 1
    					gc = cell.gs$cell.col - 1; // columns start at 1
    
    					// If both row and col falls inside start and end
    					// set the transposed cell value in the newly created columns
    					if (gc >= startColumn && gc <= endColumn &&
    						gr >= startRow && gr <= endRow) {
    						columns[gc - startColumn][gr - startRow] = cell.content.$t;
    					}
    				}
    				self.dataFound();
    			});
    		}
    	},
    	
    	/**
    	 * Find the header row. For now, we just check whether the first row contains
    	 * numbers or strings. Later we could loop down and find the first row with 
    	 * numbers.
    	 */
    	findHeaderRow: function () {
    		var headerRow = 0;
    		each(this.columns, function (column) {
    			if (typeof column[0] !== 'string') {
    				headerRow = null;
    			}
    		});
    		this.headerRow = 0;			
    	},
    	
    	/**
    	 * Trim a string from whitespace
    	 */
    	trim: function (str) {
    		return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : str;
    	},
    	
    	/**
    	 * Parse numeric cells in to number types and date types in to true dates.
    	 * @param {Object} columns
    	 */
    	parseTypes: function () {
    		var columns = this.columns,
    			col = columns.length, 
    			row,
    			val,
    			floatVal,
    			trimVal,
    			dateVal;
    			
    		while (col--) {
    			row = columns[col].length;
    			while (row--) {
    				val = columns[col][row];
    				floatVal = parseFloat(val);
    				trimVal = this.trim(val);
    
    				/*jslint eqeq: true*/
    				if (trimVal == floatVal) { // is numeric
    				/*jslint eqeq: false*/
    					columns[col][row] = floatVal;
    					
    					// If the number is greater than milliseconds in a year, assume datetime
    					if (floatVal > 365 * 24 * 3600 * 1000) {
    						columns[col].isDatetime = true;
    					} else {
    						columns[col].isNumeric = true;
    					}					
    				
    				} else { // string, continue to determine if it is a date string or really a string
    					dateVal = this.parseDate(val);
    					
    					if (col === 0 && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date
    						columns[col][row] = dateVal;
    						columns[col].isDatetime = true;
    					
    					} else { // string
    						columns[col][row] = trimVal === '' ? null : trimVal;
    					}
    				}
    				
    			}
    		}
    	},
    	//*
    	dateFormats: {
    		'YYYY-mm-dd': {
    			regex: '^([0-9]{4})-([0-9]{2})-([0-9]{2})$',
    			parser: function (match) {
    				return Date.UTC(+match[1], match[2] - 1, +match[3]);
    			}
    		}
    	},
    	// */
    	/**
    	 * Parse a date and return it as a number. Overridable through options.parseDate.
    	 */
    	parseDate: function (val) {
    		var parseDate = this.options.parseDate,
    			ret,
    			key,
    			format,
    			match;
    
    		if (parseDate) {
    			ret = parseDate(val);
    		}
    			
    		if (typeof val === 'string') {
    			for (key in this.dateFormats) {
    				format = this.dateFormats[key];
    				match = val.match(format.regex);
    				if (match) {
    					ret = format.parser(match);
    				}
    			}
    		}
    		return ret;
    	},
    	
    	/**
    	 * Reorganize rows into columns
    	 */
    	rowsToColumns: function (rows) {
    		var row,
    			rowsLength,
    			col,
    			colsLength,
    			columns;
    
    		if (rows) {
    			columns = [];
    			rowsLength = rows.length;
    			for (row = 0; row < rowsLength; row++) {
    				colsLength = rows[row].length;
    				for (col = 0; col < colsLength; col++) {
    					if (!columns[col]) {
    						columns[col] = [];
    					}
    					columns[col][row] = rows[row][col];
    				}
    			}
    		}
    		return columns;
    	},
    	
    	/**
    	 * A hook for working directly on the parsed columns
    	 */
    	parsed: function () {
    		if (this.options.parsed) {
    			this.options.parsed.call(this, this.columns);
    		}
    	},
    	
    	/**
    	 * If a complete callback function is provided in the options, interpret the 
    	 * columns into a Highcharts options object.
    	 */
    	complete: function () {
    		
    		var columns = this.columns,
    			firstCol,
    			type,
    			options = this.options,
    			valueCount,
    			series,
    			data,
    			i,
    			j,
    			seriesIndex;
    			
    		
    		if (options.complete) {
    
    			this.getColumnDistribution();
    			
    			// Use first column for X data or categories?
    			if (columns.length > 1) {
    				firstCol = columns.shift();
    				if (this.headerRow === 0) {
    					firstCol.shift(); // remove the first cell
    				}
    				
    				
    				if (firstCol.isDatetime) {
    					type = 'datetime';
    				} else if (!firstCol.isNumeric) {
    					type = 'category';
    				}
    			}
    
    			// Get the names and shift the top row
    			for (i = 0; i < columns.length; i++) {
    				if (this.headerRow === 0) {
    					columns[i].name = columns[i].shift();
    				}
    			}
    			
    			// Use the next columns for series
    			series = [];
    			for (i = 0, seriesIndex = 0; i < columns.length; seriesIndex++) {
    
    				// This series' value count
    				valueCount = Highcharts.pick(this.valueCount.individual[seriesIndex], this.valueCount.global);
    				
    				// Iterate down the cells of each column and add data to the series
    				data = [];
    				for (j = 0; j < columns[i].length; j++) {
    					data[j] = [
    						firstCol[j], 
    						columns[i][j] !== undefined ? columns[i][j] : null
    					];
    					if (valueCount > 1) {
    						data[j].push(columns[i + 1][j] !== undefined ? columns[i + 1][j] : null);
    					}
    					if (valueCount > 2) {
    						data[j].push(columns[i + 2][j] !== undefined ? columns[i + 2][j] : null);
    					}
    					if (valueCount > 3) {
    						data[j].push(columns[i + 3][j] !== undefined ? columns[i + 3][j] : null);
    					}
    					if (valueCount > 4) {
    						data[j].push(columns[i + 4][j] !== undefined ? columns[i + 4][j] : null);
    					}
    				}
    
    				// Add the series
    				series[seriesIndex] = {
    					name: columns[i].name,
    					data: data
    				};
    
    				i += valueCount;
    			}
    			
    			// Do the callback
    			options.complete({
    				xAxis: {
    					type: type
    				},
    				series: series
    			});
    		}
    	}
    	});
    	
    	// Register the Data prototype and data function on Highcharts
    	Highcharts.Data = Data;
    	Highcharts.data = function (options, chartOptions) {
    		return new Data(options, chartOptions);
    	};
    
    	// Extend Chart.init so that the Chart constructor accepts a new configuration
    	// option group, data.
    	Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, callback) {
    		var chart = this;
    
    		if (userOptions && userOptions.data) {
    			Highcharts.data(Highcharts.extend(userOptions.data, {
    				complete: function (dataOptions) {
    					
    					// Merge series configs
    					if (userOptions.series) {
    						each(userOptions.series, function (series, i) {
    							userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]);
    						});
    					}
    
    					// Do the merge
    					userOptions = Highcharts.merge(dataOptions, userOptions);
    
    					proceed.call(chart, userOptions, callback);
    				}
    			}), userOptions);
    		} else {
    			proceed.call(chart, userOptions, callback);
    		}
    	});
    
    }(Highcharts));