base.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. initStyles = function() {
  2. addCSS(defaultStyles);
  3. };
  4. $(initStyles);
  5. pending = [];
  6. $.fn.sparkline = function (userValues, userOptions) {
  7. return this.each(function () {
  8. var options = new $.fn.sparkline.options(this, userOptions),
  9. $this = $(this),
  10. render, i;
  11. render = function () {
  12. var values, width, height, tmp, mhandler, sp, vals;
  13. if (userValues === 'html' || userValues === undefined) {
  14. vals = this.getAttribute(options.get('tagValuesAttribute'));
  15. if (vals === undefined || vals === null) {
  16. vals = $this.html();
  17. }
  18. values = vals.replace(/(^\s*<!--)|(-->\s*$)|\s+/g, '').split(',');
  19. } else {
  20. values = userValues;
  21. }
  22. width = options.get('width') === 'auto' ? values.length * options.get('defaultPixelsPerValue') : options.get('width');
  23. if (options.get('height') === 'auto') {
  24. if (!options.get('composite') || !$.data(this, '_jqs_vcanvas')) {
  25. // must be a better way to get the line height
  26. tmp = document.createElement('span');
  27. tmp.innerHTML = 'a';
  28. $this.html(tmp);
  29. height = $(tmp).innerHeight() || $(tmp).height();
  30. $(tmp).remove();
  31. tmp = null;
  32. }
  33. } else {
  34. height = options.get('height');
  35. }
  36. if (!options.get('disableInteraction')) {
  37. mhandler = $.data(this, '_jqs_mhandler');
  38. if (!mhandler) {
  39. mhandler = new MouseHandler(this, options);
  40. $.data(this, '_jqs_mhandler', mhandler);
  41. } else if (!options.get('composite')) {
  42. mhandler.reset();
  43. }
  44. } else {
  45. mhandler = false;
  46. }
  47. if (options.get('composite') && !$.data(this, '_jqs_vcanvas')) {
  48. if (!$.data(this, '_jqs_errnotify')) {
  49. alert('Attempted to attach a composite sparkline to an element with no existing sparkline');
  50. $.data(this, '_jqs_errnotify', true);
  51. }
  52. return;
  53. }
  54. sp = new $.fn.sparkline[options.get('type')](this, values, options, width, height);
  55. sp.render();
  56. if (mhandler) {
  57. mhandler.registerSparkline(sp);
  58. }
  59. };
  60. if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) {
  61. if (!options.get('composite') && $.data(this, '_jqs_pending')) {
  62. // remove any existing references to the element
  63. for (i = pending.length; i; i--) {
  64. if (pending[i - 1][0] == this) {
  65. pending.splice(i - 1, 1);
  66. }
  67. }
  68. }
  69. pending.push([this, render]);
  70. $.data(this, '_jqs_pending', true);
  71. } else {
  72. render.call(this);
  73. }
  74. });
  75. };
  76. $.fn.sparkline.defaults = getDefaults();
  77. $.sparkline_display_visible = function () {
  78. var el, i, pl;
  79. var done = [];
  80. for (i = 0, pl = pending.length; i < pl; i++) {
  81. el = pending[i][0];
  82. if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
  83. pending[i][1].call(el);
  84. $.data(pending[i][0], '_jqs_pending', false);
  85. done.push(i);
  86. } else if (!$(el).closest('html').length && !$.data(el, '_jqs_pending')) {
  87. // element has been inserted and removed from the DOM
  88. // If it was not yet inserted into the dom then the .data request
  89. // will return true.
  90. // removing from the dom causes the data to be removed.
  91. $.data(pending[i][0], '_jqs_pending', false);
  92. done.push(i);
  93. }
  94. }
  95. for (i = done.length; i; i--) {
  96. pending.splice(done[i - 1], 1);
  97. }
  98. };
  99. /**
  100. * User option handler
  101. */
  102. $.fn.sparkline.options = createClass({
  103. init: function (tag, userOptions) {
  104. var extendedOptions, defaults, base, tagOptionType;
  105. this.userOptions = userOptions = userOptions || {};
  106. this.tag = tag;
  107. this.tagValCache = {};
  108. defaults = $.fn.sparkline.defaults;
  109. base = defaults.common;
  110. this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);
  111. tagOptionType = this.getTagSetting('type');
  112. if (tagOptionType === UNSET_OPTION) {
  113. extendedOptions = defaults[userOptions.type || base.type];
  114. } else {
  115. extendedOptions = defaults[tagOptionType];
  116. }
  117. this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);
  118. },
  119. getTagSetting: function (key) {
  120. var prefix = this.tagOptionsPrefix,
  121. val, i, pairs, keyval;
  122. if (prefix === false || prefix === undefined) {
  123. return UNSET_OPTION;
  124. }
  125. if (this.tagValCache.hasOwnProperty(key)) {
  126. val = this.tagValCache.key;
  127. } else {
  128. val = this.tag.getAttribute(prefix + key);
  129. if (val === undefined || val === null) {
  130. val = UNSET_OPTION;
  131. } else if (val.substr(0, 1) === '[') {
  132. val = val.substr(1, val.length - 2).split(',');
  133. for (i = val.length; i--;) {
  134. val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, ''));
  135. }
  136. } else if (val.substr(0, 1) === '{') {
  137. pairs = val.substr(1, val.length - 2).split(',');
  138. val = {};
  139. for (i = pairs.length; i--;) {
  140. keyval = pairs[i].split(':', 2);
  141. val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, ''));
  142. }
  143. } else {
  144. val = normalizeValue(val);
  145. }
  146. this.tagValCache.key = val;
  147. }
  148. return val;
  149. },
  150. get: function (key, defaultval) {
  151. var tagOption = this.getTagSetting(key),
  152. result;
  153. if (tagOption !== UNSET_OPTION) {
  154. return tagOption;
  155. }
  156. return (result = this.mergedOptions[key]) === undefined ? defaultval : result;
  157. }
  158. });
  159. $.fn.sparkline._base = createClass({
  160. disabled: false,
  161. init: function (el, values, options, width, height) {
  162. this.el = el;
  163. this.$el = $(el);
  164. this.values = values;
  165. this.options = options;
  166. this.width = width;
  167. this.height = height;
  168. this.currentRegion = undefined;
  169. },
  170. /**
  171. * Setup the canvas
  172. */
  173. initTarget: function () {
  174. var interactive = !this.options.get('disableInteraction');
  175. if (!(this.target = this.$el.simpledraw(this.width, this.height, this.options.get('composite'), interactive))) {
  176. this.disabled = true;
  177. } else {
  178. this.canvasWidth = this.target.pixelWidth;
  179. this.canvasHeight = this.target.pixelHeight;
  180. }
  181. },
  182. /**
  183. * Actually render the chart to the canvas
  184. */
  185. render: function () {
  186. if (this.disabled) {
  187. this.el.innerHTML = '';
  188. return false;
  189. }
  190. return true;
  191. },
  192. /**
  193. * Return a region id for a given x/y co-ordinate
  194. */
  195. getRegion: function (x, y) {
  196. },
  197. /**
  198. * Highlight an item based on the moused-over x,y co-ordinate
  199. */
  200. setRegionHighlight: function (el, x, y) {
  201. var currentRegion = this.currentRegion,
  202. highlightEnabled = !this.options.get('disableHighlight'),
  203. newRegion;
  204. if (x > this.canvasWidth || y > this.canvasHeight || x < 0 || y < 0) {
  205. return null;
  206. }
  207. newRegion = this.getRegion(el, x, y);
  208. if (currentRegion !== newRegion) {
  209. if (currentRegion !== undefined && highlightEnabled) {
  210. this.removeHighlight();
  211. }
  212. this.currentRegion = newRegion;
  213. if (newRegion !== undefined && highlightEnabled) {
  214. this.renderHighlight();
  215. }
  216. return true;
  217. }
  218. return false;
  219. },
  220. /**
  221. * Reset any currently highlighted item
  222. */
  223. clearRegionHighlight: function () {
  224. if (this.currentRegion !== undefined) {
  225. this.removeHighlight();
  226. this.currentRegion = undefined;
  227. return true;
  228. }
  229. return false;
  230. },
  231. renderHighlight: function () {
  232. this.changeHighlight(true);
  233. },
  234. removeHighlight: function () {
  235. this.changeHighlight(false);
  236. },
  237. changeHighlight: function (highlight) {},
  238. /**
  239. * Fetch the HTML to display as a tooltip
  240. */
  241. getCurrentRegionTooltip: function () {
  242. var options = this.options,
  243. header = '',
  244. entries = [],
  245. fields, formats, formatlen, fclass, text, i,
  246. showFields, showFieldsKey, newFields, fv,
  247. formatter, format, fieldlen, j;
  248. if (this.currentRegion === undefined) {
  249. return '';
  250. }
  251. fields = this.getCurrentRegionFields();
  252. formatter = options.get('tooltipFormatter');
  253. if (formatter) {
  254. return formatter(this, options, fields);
  255. }
  256. if (options.get('tooltipChartTitle')) {
  257. header += '<div class="jqs jqstitle">' + options.get('tooltipChartTitle') + '</div>\n';
  258. }
  259. formats = this.options.get('tooltipFormat');
  260. if (!formats) {
  261. return '';
  262. }
  263. if (!$.isArray(formats)) {
  264. formats = [formats];
  265. }
  266. if (!$.isArray(fields)) {
  267. fields = [fields];
  268. }
  269. showFields = this.options.get('tooltipFormatFieldlist');
  270. showFieldsKey = this.options.get('tooltipFormatFieldlistKey');
  271. if (showFields && showFieldsKey) {
  272. // user-selected ordering of fields
  273. newFields = [];
  274. for (i = fields.length; i--;) {
  275. fv = fields[i][showFieldsKey];
  276. if ((j = $.inArray(fv, showFields)) != -1) {
  277. newFields[j] = fields[i];
  278. }
  279. }
  280. fields = newFields;
  281. }
  282. formatlen = formats.length;
  283. fieldlen = fields.length;
  284. for (i = 0; i < formatlen; i++) {
  285. format = formats[i];
  286. if (typeof format === 'string') {
  287. format = new SPFormat(format);
  288. }
  289. fclass = format.fclass || 'jqsfield';
  290. for (j = 0; j < fieldlen; j++) {
  291. if (!fields[j].isNull || !options.get('tooltipSkipNull')) {
  292. $.extend(fields[j], {
  293. prefix: options.get('tooltipPrefix'),
  294. suffix: options.get('tooltipSuffix')
  295. });
  296. text = format.render(fields[j], options.get('tooltipValueLookups'), options);
  297. entries.push('<div class="' + fclass + '">' + text + '</div>');
  298. }
  299. }
  300. }
  301. if (entries.length) {
  302. return header + entries.join('\n');
  303. }
  304. return '';
  305. },
  306. getCurrentRegionFields: function () {},
  307. calcHighlightColor: function (color, options) {
  308. var highlightColor = options.get('highlightColor'),
  309. lighten = options.get('highlightLighten'),
  310. parse, mult, rgbnew, i;
  311. if (highlightColor) {
  312. return highlightColor;
  313. }
  314. if (lighten) {
  315. // extract RGB values
  316. parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
  317. if (parse) {
  318. rgbnew = [];
  319. mult = color.length === 4 ? 16 : 1;
  320. for (i = 0; i < 3; i++) {
  321. rgbnew[i] = clipval(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255);
  322. }
  323. return 'rgb(' + rgbnew.join(',') + ')';
  324. }
  325. }
  326. return color;
  327. }
  328. });
  329. barHighlightMixin = {
  330. changeHighlight: function (highlight) {
  331. var currentRegion = this.currentRegion,
  332. target = this.target,
  333. shapeids = this.regionShapes[currentRegion],
  334. newShapes;
  335. // will be null if the region value was null
  336. if (shapeids) {
  337. newShapes = this.renderRegion(currentRegion, highlight);
  338. if ($.isArray(newShapes) || $.isArray(shapeids)) {
  339. target.replaceWithShapes(shapeids, newShapes);
  340. this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) {
  341. return newShape.id;
  342. });
  343. } else {
  344. target.replaceWithShape(shapeids, newShapes);
  345. this.regionShapes[currentRegion] = newShapes.id;
  346. }
  347. }
  348. },
  349. render: function () {
  350. var values = this.values,
  351. target = this.target,
  352. regionShapes = this.regionShapes,
  353. shapes, ids, i, j;
  354. if (!this.cls._super.render.call(this)) {
  355. return;
  356. }
  357. for (i = values.length; i--;) {
  358. shapes = this.renderRegion(i);
  359. if (shapes) {
  360. if ($.isArray(shapes)) {
  361. ids = [];
  362. for (j = shapes.length; j--;) {
  363. shapes[j].append();
  364. ids.push(shapes[j].id);
  365. }
  366. regionShapes[i] = ids;
  367. } else {
  368. shapes.append();
  369. regionShapes[i] = shapes.id; // store just the shapeid
  370. }
  371. } else {
  372. // null value
  373. regionShapes[i] = null;
  374. }
  375. }
  376. target.render();
  377. }
  378. };