From 0fcd7eb07e38bbc7a7713446506ede694b13b1e3 Mon Sep 17 00:00:00 2001 From: Warren Volz Date: Mon, 5 Apr 2021 15:26:20 -0600 Subject: [PATCH 01/34] Correct dxcc exceptions table name --- application/models/Dok.php | 2 +- application/models/Dxcc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/models/Dok.php b/application/models/Dok.php index 4e7618a8..2a9653e5 100644 --- a/application/models/Dok.php +++ b/application/models/Dok.php @@ -95,7 +95,7 @@ class DOK extends CI_Model { { $exceptions = $this->db->query(' SELECT * - FROM `dxccexceptions` + FROM `dxcc_exceptions` WHERE `prefix` = \''.$callsign.'\' LIMIT 1 '); diff --git a/application/models/Dxcc.php b/application/models/Dxcc.php index bce17ea3..261550b1 100644 --- a/application/models/Dxcc.php +++ b/application/models/Dxcc.php @@ -137,7 +137,7 @@ class DXCC extends CI_Model { { $exceptions = $this->db->query(' SELECT * - FROM `dxccexceptions` + FROM `dxcc_exceptions` WHERE `prefix` = \''.$callsign.'\' LIMIT 1 '); From 346b28754edd4ae48a23105f8cd609d2f7c20321 Mon Sep 17 00:00:00 2001 From: Warren Volz Date: Sat, 10 Apr 2021 00:19:02 -0600 Subject: [PATCH 02/34] fix display lookup so Nebraska looks for NE not ME --- application/views/qso/edit_ajax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/qso/edit_ajax.php b/application/views/qso/edit_ajax.php index be83361c..54725d53 100644 --- a/application/views/qso/edit_ajax.php +++ b/application/views/qso/edit_ajax.php @@ -344,7 +344,7 @@ - + From fdcf04b11724f498e14aab8724123d67f0d66484 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 21 Apr 2021 12:23:01 +0200 Subject: [PATCH 03/34] [Adif export] Bugfix. Programversion showed incorrect length. Fixed line endings in view. Fixes #997. --- application/views/adif/data/clublog.php | 6 +++--- application/views/adif/data/exportall.php | 4 ++-- application/views/adif/data/exportsat.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/application/views/adif/data/clublog.php b/application/views/adif/data/clublog.php index d8bb419a..14e84471 100644 --- a/application/views/adif/data/clublog.php +++ b/application/views/adif/data/clublog.php @@ -1,6 +1,6 @@ 3.1.0 -config->item('app_name')); ?>>config->item('app_name')."\n"; ?> -config->item('app_version')); ?>>Version config->item('app_version')."\n"; ?> +config->item('app_name')); ?>>config->item('app_name')."\r\n"; ?> +config->item('app_version')); ?>>Version config->item('app_version')."\r\n"; ?> load->library('AdifHelper'); foreach ($qsos->result() as $qso) { echo $CI->adifhelper->getAdifLine($qso); -} \ No newline at end of file +} diff --git a/application/views/adif/data/exportall.php b/application/views/adif/data/exportall.php index 26991708..220ab293 100644 --- a/application/views/adif/data/exportall.php +++ b/application/views/adif/data/exportall.php @@ -3,8 +3,8 @@ header('Content-Disposition: attachment; filename="'.$this->session->userdata('user_callsign').'-'.date('dmY-Hi').'.adi"') ?> 3.1.0 -config->item('app_name')); ?>>config->item('app_name')."\n"; ?> -config->item('app_version')); ?>>Version config->item('app_version')."\n"; ?> +config->item('app_name')); ?>>config->item('app_name')."\r\n"; ?> +config->item('app_version')); ?>>Version config->item('app_version')."\r\n"; ?> session->userdata('user_callsign').'-'.date('dmY-Hi').'.adi"') ?> 3.1.0 -config->item('app_name')); ?>>config->item('app_name')."\n"; ?> -config->item('app_version')); ?>>Version config->item('app_version')."\n"; ?> +config->item('app_name')); ?>>config->item('app_name')."\r\n"; ?> +config->item('app_version')); ?>>Version config->item('app_version')."\r\n"; ?> Date: Wed, 28 Apr 2021 16:14:15 +0100 Subject: [PATCH 04/34] [Satellites] Changed CAS-6 to TO-108 --- assets/json/satellite_data.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/json/satellite_data.json b/assets/json/satellite_data.json index 0cad7907..6e6a714d 100644 --- a/assets/json/satellite_data.json +++ b/assets/json/satellite_data.json @@ -143,7 +143,7 @@ ] } }, - "CAS-6":{ + "TO-108":{ "Modes":{ "U/V":[ { From 31b678852005f02868868000a2dfd5a8f17dae9e Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 29 Apr 2021 22:38:55 +0100 Subject: [PATCH 05/34] [WAS]{SATS} This adds a demo of US State Map showing worked, confirmed This adds US State map functions to Cloudlog at the moment SATS only. Can be found at awards/was_map Using https://newsignature.github.io/us-map/ --- application/controllers/Awards.php | 26 + application/views/awards/was/map.php | 82 + assets/js/color.jquery.js | 123 + assets/js/jquery.usmap.js | 656 +++ assets/js/raphael.js | 5381 +++++++++++++++++++++++ assets/svg/Blank_USA,_w_territories.svg | 142 + assets/svg/Blank_US_Map.svg | 326 ++ 7 files changed, 6736 insertions(+) create mode 100644 application/views/awards/was/map.php create mode 100755 assets/js/color.jquery.js create mode 100644 assets/js/jquery.usmap.js create mode 100755 assets/js/raphael.js create mode 100755 assets/svg/Blank_USA,_w_territories.svg create mode 100644 assets/svg/Blank_US_Map.svg diff --git a/application/controllers/Awards.php b/application/controllers/Awards.php index 02748542..e8df3659 100644 --- a/application/controllers/Awards.php +++ b/application/controllers/Awards.php @@ -492,4 +492,30 @@ class Awards extends CI_Controller { $this->load->view('adif/data/exportall', $data); } + + public function was_map() { + + $this->load->model('was'); + $data['worked_bands'] = $this->was->get_worked_bands(); + + + $bands[] = 'SAT'; + + + $data['bands'] = $bands; // Used for displaying selected band(s) in the table in the view + + $postdata['lotw'] = 1; + $postdata['qsl'] = 1; + $postdata['worked'] = 1; + $postdata['confirmed'] = 1; + $postdata['notworked'] = 1; + $postdata['band'] = 'SAT'; + + + $data['was_array'] = $this->was->get_was_array($bands, $postdata); + $data['was_summary'] = $this->was->get_was_summary($bands); + + $data['page_title'] = ""; + $this->load->view('awards/was/map', $data); + } } diff --git a/application/views/awards/was/map.php b/application/views/awards/was/map.php new file mode 100644 index 00000000..4b486b23 --- /dev/null +++ b/application/views/awards/was/map.php @@ -0,0 +1,82 @@ + + + + US Map Demo + + + + + + + + + + + +
+ + diff --git a/assets/js/color.jquery.js b/assets/js/color.jquery.js new file mode 100755 index 00000000..12624027 --- /dev/null +++ b/assets/js/color.jquery.js @@ -0,0 +1,123 @@ +/* + * jQuery Color Animations + * Copyright 2007 John Resig + * Released under the MIT and GPL licenses. + */ + +(function(jQuery){ + + // We override the animation for all of these color styles + jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){ + jQuery.fx.step[attr] = function(fx){ + if ( fx.state == 0 ) { + fx.start = getColor( fx.elem, attr ); + fx.end = getRGB( fx.end ); + } + + fx.elem.style[attr] = "rgb(" + [ + Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0), + Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0), + Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0) + ].join(",") + ")"; + } + }); + + // Color Conversion functions from highlightFade + // By Blair Mitchelmore + // http://jquery.offput.ca/highlightFade/ + + // Parse strings looking for color tuples [255,255,255] + function getRGB(color) { + var result; + + // Check if we're already dealing with an array of colors + if ( color && color.constructor == Array && color.length == 3 ) + return color; + + // Look for rgb(num,num,num) + if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) + return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])]; + + // Look for rgb(num%,num%,num%) + if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) + return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55]; + + // Look for #a0b1c2 + if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) + return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)]; + + // Look for #fff + if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) + return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)]; + + // Otherwise, we're most likely dealing with a named color + return colors[jQuery.trim(color).toLowerCase()]; + } + + function getColor(elem, attr) { + var color; + + do { + color = jQuery.curCSS(elem, attr); + + // Keep going until we find an element that has color, or we hit the body + if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") ) + break; + + attr = "backgroundColor"; + } while ( elem = elem.parentNode ); + + return getRGB(color); + }; + + // Some named colors to work with + // From Interface by Stefan Petre + // http://interface.eyecon.ro/ + + var colors = { + aqua:[0,255,255], + azure:[240,255,255], + beige:[245,245,220], + black:[0,0,0], + blue:[0,0,255], + brown:[165,42,42], + cyan:[0,255,255], + darkblue:[0,0,139], + darkcyan:[0,139,139], + darkgrey:[169,169,169], + darkgreen:[0,100,0], + darkkhaki:[189,183,107], + darkmagenta:[139,0,139], + darkolivegreen:[85,107,47], + darkorange:[255,140,0], + darkorchid:[153,50,204], + darkred:[139,0,0], + darksalmon:[233,150,122], + darkviolet:[148,0,211], + fuchsia:[255,0,255], + gold:[255,215,0], + green:[0,128,0], + indigo:[75,0,130], + khaki:[240,230,140], + lightblue:[173,216,230], + lightcyan:[224,255,255], + lightgreen:[144,238,144], + lightgrey:[211,211,211], + lightpink:[255,182,193], + lightyellow:[255,255,224], + lime:[0,255,0], + magenta:[255,0,255], + maroon:[128,0,0], + navy:[0,0,128], + olive:[128,128,0], + orange:[255,165,0], + pink:[255,192,203], + purple:[128,0,128], + violet:[128,0,128], + red:[255,0,0], + silver:[192,192,192], + white:[255,255,255], + yellow:[255,255,0] + }; + +})(jQuery); \ No newline at end of file diff --git a/assets/js/jquery.usmap.js b/assets/js/jquery.usmap.js new file mode 100644 index 00000000..0b2b1fee --- /dev/null +++ b/assets/js/jquery.usmap.js @@ -0,0 +1,656 @@ +(function($, document, window, Raphael, undefined) { + // jQuery Plugin Factory + function jQueryPluginFactory( $, name, methods, getters ){ + getters = getters instanceof Array ? getters : []; + var getters_obj = {}; + for(var i=0; i values - tolerance) { + return value - rem + values; + } + } + return value; + }; + + + var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) { + return function () { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase(); + }; + })(/[xy]/g, function (c) { + var r = math.random() * 16 | 0, + v = c == "x" ? r : (r & 3 | 8); + return v.toString(16); + }); + + + R.setWindow = function (newwin) { + eve("setWindow", R, g.win, newwin); + g.win = newwin; + g.doc = g.win.document; + if (initWin) { + initWin(g.win); + } + }; + var toHex = function (color) { + if (R.vml) { + // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/ + var trim = /^\s+|\s+$/g; + var bod; + try { + var docum = new ActiveXObject("htmlfile"); + docum.write(""); + docum.close(); + bod = docum.body; + } catch(e) { + bod = createPopup().document.body; + } + var range = bod.createTextRange(); + toHex = cacher(function (color) { + try { + bod.style.color = Str(color).replace(trim, E); + var value = range.queryCommandValue("ForeColor"); + value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16); + return "#" + ("000000" + value.toString(16)).slice(-6); + } catch(e) { + return "none"; + } + }); + } else { + var i = g.doc.createElement("i"); + i.title = "Rapha\xebl Colour Picker"; + i.style.display = "none"; + g.doc.body.appendChild(i); + toHex = cacher(function (color) { + i.style.color = color; + return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color"); + }); + } + return toHex(color); + }, + hsbtoString = function () { + return "hsb(" + [this.h, this.s, this.b] + ")"; + }, + hsltoString = function () { + return "hsl(" + [this.h, this.s, this.l] + ")"; + }, + rgbtoString = function () { + return this.hex; + }, + prepareRGB = function (r, g, b) { + if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) { + b = r.b; + g = r.g; + r = r.r; + } + if (g == null && R.is(r, string)) { + var clr = R.getRGB(r); + r = clr.r; + g = clr.g; + b = clr.b; + } + if (r > 1 || g > 1 || b > 1) { + r /= 255; + g /= 255; + b /= 255; + } + + return [r, g, b]; + }, + packageRGB = function (r, g, b, o) { + r *= 255; + g *= 255; + b *= 255; + var rgb = { + r: r, + g: g, + b: b, + hex: R.rgb(r, g, b), + toString: rgbtoString + }; + R.is(o, "finite") && (rgb.opacity = o); + return rgb; + }; + + + R.color = function (clr) { + var rgb; + if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) { + rgb = R.hsb2rgb(clr); + clr.r = rgb.r; + clr.g = rgb.g; + clr.b = rgb.b; + clr.hex = rgb.hex; + } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) { + rgb = R.hsl2rgb(clr); + clr.r = rgb.r; + clr.g = rgb.g; + clr.b = rgb.b; + clr.hex = rgb.hex; + } else { + if (R.is(clr, "string")) { + clr = R.getRGB(clr); + } + if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) { + rgb = R.rgb2hsl(clr); + clr.h = rgb.h; + clr.s = rgb.s; + clr.l = rgb.l; + rgb = R.rgb2hsb(clr); + clr.v = rgb.b; + } else { + clr = {hex: "none"}; + crl.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1; + } + } + clr.toString = rgbtoString; + return clr; + }; + + R.hsb2rgb = function (h, s, v, o) { + if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) { + v = h.b; + s = h.s; + h = h.h; + o = h.o; + } + h *= 360; + var R, G, B, X, C; + h = (h % 360) / 60; + C = v * s; + X = C * (1 - abs(h % 2 - 1)); + R = G = B = v - C; + + h = ~~h; + R += [C, X, 0, 0, X, C][h]; + G += [X, C, C, X, 0, 0][h]; + B += [0, 0, X, C, C, X][h]; + return packageRGB(R, G, B, o); + }; + + R.hsl2rgb = function (h, s, l, o) { + if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) { + l = h.l; + s = h.s; + h = h.h; + } + if (h > 1 || s > 1 || l > 1) { + h /= 360; + s /= 100; + l /= 100; + } + h *= 360; + var R, G, B, X, C; + h = (h % 360) / 60; + C = 2 * s * (l < .5 ? l : 1 - l); + X = C * (1 - abs(h % 2 - 1)); + R = G = B = l - C / 2; + + h = ~~h; + R += [C, X, 0, 0, X, C][h]; + G += [X, C, C, X, 0, 0][h]; + B += [0, 0, X, C, C, X][h]; + return packageRGB(R, G, B, o); + }; + + R.rgb2hsb = function (r, g, b) { + b = prepareRGB(r, g, b); + r = b[0]; + g = b[1]; + b = b[2]; + + var H, S, V, C; + V = mmax(r, g, b); + C = V - mmin(r, g, b); + H = (C == 0 ? null : + V == r ? (g - b) / C : + V == g ? (b - r) / C + 2 : + (r - g) / C + 4 + ); + H = ((H + 360) % 6) * 60 / 360; + S = C == 0 ? 0 : C / V; + return {h: H, s: S, b: V, toString: hsbtoString}; + }; + + R.rgb2hsl = function (r, g, b) { + b = prepareRGB(r, g, b); + r = b[0]; + g = b[1]; + b = b[2]; + + var H, S, L, M, m, C; + M = mmax(r, g, b); + m = mmin(r, g, b); + C = M - m; + H = (C == 0 ? null : + M == r ? (g - b) / C : + M == g ? (b - r) / C + 2 : + (r - g) / C + 4); + H = ((H + 360) % 6) * 60 / 360; + L = (M + m) / 2; + S = (C == 0 ? 0 : + L < .5 ? C / (2 * L) : + C / (2 - 2 * L)); + return {h: H, s: S, l: L, toString: hsltoString}; + }; + R._path2string = function () { + return this.join(",").replace(p2s, "$1"); + }; + function repush(array, item) { + for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) { + return array.push(array.splice(i, 1)[0]); + } + } + function cacher(f, scope, postprocessor) { + function newf() { + var arg = Array.prototype.slice.call(arguments, 0), + args = arg.join("\u2400"), + cache = newf.cache = newf.cache || {}, + count = newf.count = newf.count || []; + if (cache[has](args)) { + repush(count, args); + return postprocessor ? postprocessor(cache[args]) : cache[args]; + } + count.length >= 1e3 && delete cache[count.shift()]; + count.push(args); + cache[args] = f[apply](scope, arg); + return postprocessor ? postprocessor(cache[args]) : cache[args]; + } + return newf; + } + + var preload = R._preload = function (src, f) { + var img = g.doc.createElement("img"); + img.style.cssText = "position:absolute;left:-9999em;top-9999em"; + img.onload = function () { + f.call(this); + this.onload = null; + g.doc.body.removeChild(this); + }; + img.onerror = function () { + g.doc.body.removeChild(this); + }; + g.doc.body.appendChild(img); + img.src = src; + }; + + function clrToString() { + return this.hex; + } + + + R.getRGB = cacher(function (colour) { + if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) { + return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString}; + } + if (colour == "none") { + return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString}; + } + !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour)); + var res, + red, + green, + blue, + opacity, + t, + values, + rgb = colour.match(colourRegExp); + if (rgb) { + if (rgb[2]) { + blue = toInt(rgb[2].substring(5), 16); + green = toInt(rgb[2].substring(3, 5), 16); + red = toInt(rgb[2].substring(1, 3), 16); + } + if (rgb[3]) { + blue = toInt((t = rgb[3].charAt(3)) + t, 16); + green = toInt((t = rgb[3].charAt(2)) + t, 16); + red = toInt((t = rgb[3].charAt(1)) + t, 16); + } + if (rgb[4]) { + values = rgb[4].split(commaSpaces); + red = toFloat(values[0]); + values[0].slice(-1) == "%" && (red *= 2.55); + green = toFloat(values[1]); + values[1].slice(-1) == "%" && (green *= 2.55); + blue = toFloat(values[2]); + values[2].slice(-1) == "%" && (blue *= 2.55); + rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3])); + values[3] && values[3].slice(-1) == "%" && (opacity /= 100); + } + if (rgb[5]) { + values = rgb[5].split(commaSpaces); + red = toFloat(values[0]); + values[0].slice(-1) == "%" && (red *= 2.55); + green = toFloat(values[1]); + values[1].slice(-1) == "%" && (green *= 2.55); + blue = toFloat(values[2]); + values[2].slice(-1) == "%" && (blue *= 2.55); + (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360); + rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3])); + values[3] && values[3].slice(-1) == "%" && (opacity /= 100); + return R.hsb2rgb(red, green, blue, opacity); + } + if (rgb[6]) { + values = rgb[6].split(commaSpaces); + red = toFloat(values[0]); + values[0].slice(-1) == "%" && (red *= 2.55); + green = toFloat(values[1]); + values[1].slice(-1) == "%" && (green *= 2.55); + blue = toFloat(values[2]); + values[2].slice(-1) == "%" && (blue *= 2.55); + (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360); + rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3])); + values[3] && values[3].slice(-1) == "%" && (opacity /= 100); + return R.hsl2rgb(red, green, blue, opacity); + } + rgb = {r: red, g: green, b: blue, toString: clrToString}; + rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1); + R.is(opacity, "finite") && (rgb.opacity = opacity); + return rgb; + } + return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString}; + }, R); + + R.hsb = cacher(function (h, s, b) { + return R.hsb2rgb(h, s, b).hex; + }); + + R.hsl = cacher(function (h, s, l) { + return R.hsl2rgb(h, s, l).hex; + }); + + R.rgb = cacher(function (r, g, b) { + return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1); + }); + + R.getColor = function (value) { + var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75}, + rgb = this.hsb2rgb(start.h, start.s, start.b); + start.h += .075; + if (start.h > 1) { + start.h = 0; + start.s -= .2; + start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b}); + } + return rgb.hex; + }; + + R.getColor.reset = function () { + delete this.start; + }; + + // http://schepers.cc/getting-to-the-point + function catmullRom2bezier(crp) { + var d = []; + for (var i = 0, iLen = crp.length; iLen - 2 > i; i += 2) { + var p = [{x: +crp[i], y: +crp[i + 1]}, + {x: +crp[i], y: +crp[i + 1]}, + {x: +crp[i + 2], y: +crp[i + 3]}, + {x: +crp[i + 4], y: +crp[i + 5]}]; + if (iLen - 4 == i) { + p[0] = {x: +crp[i - 2], y: +crp[i - 1]}; + p[3] = p[2]; + } else if (i) { + p[0] = {x: +crp[i - 2], y: +crp[i - 1]}; + } + d.push(["C", + (-p[0].x + 6 * p[1].x + p[2].x) / 6, + (-p[0].y + 6 * p[1].y + p[2].y) / 6, + (p[1].x + 6 * p[2].x - p[3].x) / 6, + (p[1].y + 6*p[2].y - p[3].y) / 6, + p[2].x, + p[2].y + ]); + } + + return d; + } + + R.parsePathString = cacher(function (pathString) { + if (!pathString) { + return null; + } + var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0}, + data = []; + if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption + data = pathClone(pathString); + } + if (!data.length) { + Str(pathString).replace(pathCommand, function (a, b, c) { + var params = [], + name = b.toLowerCase(); + c.replace(pathValues, function (a, b) { + b && params.push(+b); + }); + if (name == "m" && params.length > 2) { + data.push([b][concat](params.splice(0, 2))); + name = "l"; + b = b == "m" ? "l" : "L"; + } + if (name == "r") { + data.push([b][concat](params)); + } else while (params.length >= paramCounts[name]) { + data.push([b][concat](params.splice(0, paramCounts[name]))); + if (!paramCounts[name]) { + break; + } + } + }); + } + data.toString = R._path2string; + return data; + }); + + R.parseTransformString = cacher(function (TString) { + if (!TString) { + return null; + } + var paramCounts = {r: 3, s: 4, t: 2, m: 6}, + data = []; + if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption + data = pathClone(TString); + } + if (!data.length) { + Str(TString).replace(tCommand, function (a, b, c) { + var params = [], + name = lowerCase.call(b); + c.replace(pathValues, function (a, b) { + b && params.push(+b); + }); + data.push([name][concat](params)); + }); + } + data.toString = R._path2string; + return data; + }); + + R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t, + t13 = pow(t1, 3), + t12 = pow(t1, 2), + t2 = t * t, + t3 = t2 * t, + x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, + y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y, + mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), + my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), + nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), + ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), + ax = t1 * p1x + t * c1x, + ay = t1 * p1y + t * c1y, + cx = t1 * c2x + t * p2x, + cy = t1 * c2y + t * p2y, + alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI); + (mx > nx || my < ny) && (alpha += 180); + return { + x: x, + y: y, + m: {x: mx, y: my}, + n: {x: nx, y: ny}, + start: {x: ax, y: ay}, + end: {x: cx, y: cy}, + alpha: alpha + }; + }; + var pathDimensions = cacher(function (path) { + if (!path) { + return {x: 0, y: 0, width: 0, height: 0}; + } + path = path2curve(path); + var x = 0, + y = 0, + X = [], + Y = [], + p; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] == "M") { + x = p[1]; + y = p[2]; + X.push(x); + Y.push(y); + } else { + var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + X = X[concat](dim.min.x, dim.max.x); + Y = Y[concat](dim.min.y, dim.max.y); + x = p[5]; + y = p[6]; + } + } + var xmin = mmin[apply](0, X), + ymin = mmin[apply](0, Y); + return { + x: xmin, + y: ymin, + width: mmax[apply](0, X) - xmin, + height: mmax[apply](0, Y) - ymin + }; + }, null, function (o) { + return { + x: o.x, + y: o.y, + width: o.width, + height: o.height + }; + }), + pathClone = function (pathArray) { + var res = []; + if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption + pathArray = R.parsePathString(pathArray); + } + for (var i = 0, ii = pathArray.length; i < ii; i++) { + res[i] = []; + for (var j = 0, jj = pathArray[i].length; j < jj; j++) { + res[i][j] = pathArray[i][j]; + } + } + res.toString = R._path2string; + return res; + }, + pathToRelative = R._pathToRelative = cacher(function (pathArray) { + if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption + pathArray = R.parsePathString(pathArray); + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0; + if (pathArray[0][0] == "M") { + x = pathArray[0][1]; + y = pathArray[0][2]; + mx = x; + my = y; + start++; + res.push(["M", x, y]); + } + for (var i = start, ii = pathArray.length; i < ii; i++) { + var r = res[i] = [], + pa = pathArray[i]; + if (pa[0] != lowerCase.call(pa[0])) { + r[0] = lowerCase.call(pa[0]); + switch (r[0]) { + case "a": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] - x).toFixed(3); + r[7] = +(pa[7] - y).toFixed(3); + break; + case "v": + r[1] = +(pa[1] - y).toFixed(3); + break; + case "m": + mx = pa[1]; + my = pa[2]; + default: + for (var j = 1, jj = pa.length; j < jj; j++) { + r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); + } + } + } else { + r = res[i] = []; + if (pa[0] == "m") { + mx = pa[1] + x; + my = pa[2] + y; + } + for (var k = 0, kk = pa.length; k < kk; k++) { + res[i][k] = pa[k]; + } + } + var len = res[i].length; + switch (res[i][0]) { + case "z": + x = mx; + y = my; + break; + case "h": + x += +res[i][len - 1]; + break; + case "v": + y += +res[i][len - 1]; + break; + default: + x += +res[i][len - 2]; + y += +res[i][len - 1]; + } + } + res.toString = R._path2string; + return res; + }, 0, pathClone), + pathToAbsolute = R._pathToAbsolute = cacher(function (pathArray) { + if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption + pathArray = R.parsePathString(pathArray); + } + if (!pathArray || !pathArray.length) { + return [["M", 0, 0]]; + } + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0; + if (pathArray[0][0] == "M") { + x = +pathArray[0][1]; + y = +pathArray[0][2]; + mx = x; + my = y; + start++; + res[0] = ["M", x, y]; + } + for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { + res.push(r = []); + pa = pathArray[i]; + if (pa[0] != upperCase.call(pa[0])) { + r[0] = upperCase.call(pa[0]); + switch (r[0]) { + case "A": + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +(pa[6] + x); + r[7] = +(pa[7] + y); + break; + case "V": + r[1] = +pa[1] + y; + break; + case "H": + r[1] = +pa[1] + x; + break; + case "R": + var dots = [x, y][concat](pa.slice(1)); + for (var j = 2, jj = dots.length; j < jj; j++) { + dots[j] = +dots[j] + x; + dots[++j] = +dots[j] + y; + } + res.pop(); + res = res[concat](catmullRom2bezier(dots)); + break; + case "M": + mx = +pa[1] + x; + my = +pa[2] + y; + default: + for (j = 1, jj = pa.length; j < jj; j++) { + r[j] = +pa[j] + ((j % 2) ? x : y); + } + } + } else if (pa[0] == "R") { + dots = [x, y][concat](pa.slice(1)); + res.pop(); + res = res[concat](catmullRom2bezier(dots)); + r = ["R"][concat](pa.slice(-2)); + } else { + for (var k = 0, kk = pa.length; k < kk; k++) { + r[k] = pa[k]; + } + } + switch (r[0]) { + case "Z": + x = mx; + y = my; + break; + case "H": + x = r[1]; + break; + case "V": + y = r[1]; + break; + case "M": + mx = r[r.length - 2]; + my = r[r.length - 1]; + default: + x = r[r.length - 2]; + y = r[r.length - 1]; + } + } + res.toString = R._path2string; + return res; + }, null, pathClone), + l2c = function (x1, y1, x2, y2) { + return [x1, y1, x2, y2, x2, y2]; + }, + q2c = function (x1, y1, ax, ay, x2, y2) { + var _13 = 1 / 3, + _23 = 2 / 3; + return [ + _13 * x1 + _23 * ax, + _13 * y1 + _23 * ay, + _13 * x2 + _23 * ax, + _13 * y2 + _23 * ay, + x2, + y2 + ]; + }, + a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { + // for more information of where this math came from visit: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + var _120 = PI * 120 / 180, + rad = PI / 180 * (+angle || 0), + res = [], + xy, + rotate = cacher(function (x, y, rad) { + var X = x * math.cos(rad) - y * math.sin(rad), + Y = x * math.sin(rad) + y * math.cos(rad); + return {x: X, y: Y}; + }); + if (!recursive) { + xy = rotate(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = rotate(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + var cos = math.cos(PI / 180 * angle), + sin = math.sin(PI / 180 * angle), + x = (x1 - x2) / 2, + y = (y1 - y2) / 2; + var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + if (h > 1) { + h = math.sqrt(h); + rx = h * rx; + ry = h * ry; + } + var rx2 = rx * rx, + ry2 = ry * ry, + k = (large_arc_flag == sweep_flag ? -1 : 1) * + math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), + cx = k * rx * y / ry + (x1 + x2) / 2, + cy = k * -ry * x / rx + (y1 + y2) / 2, + f1 = math.asin(((y1 - cy) / ry).toFixed(9)), + f2 = math.asin(((y2 - cy) / ry).toFixed(9)); + + f1 = x1 < cx ? PI - f1 : f1; + f2 = x2 < cx ? PI - f2 : f2; + f1 < 0 && (f1 = PI * 2 + f1); + f2 < 0 && (f2 = PI * 2 + f2); + if (sweep_flag && f1 > f2) { + f1 = f1 - PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - PI * 2; + } + } else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + var df = f2 - f1; + if (abs(df) > _120) { + var f2old = f2, + x2old = x2, + y2old = y2; + f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); + x2 = cx + rx * math.cos(f2); + y2 = cy + ry * math.sin(f2); + res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); + } + df = f2 - f1; + var c1 = math.cos(f1), + s1 = math.sin(f1), + c2 = math.cos(f2), + s2 = math.sin(f2), + t = math.tan(df / 4), + hx = 4 / 3 * rx * t, + hy = 4 / 3 * ry * t, + m1 = [x1, y1], + m2 = [x1 + hx * s1, y1 - hy * c1], + m3 = [x2 + hx * s2, y2 - hy * c2], + m4 = [x2, y2]; + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + if (recursive) { + return [m2, m3, m4][concat](res); + } else { + res = [m2, m3, m4][concat](res).join().split(","); + var newres = []; + for (var i = 0, ii = res.length; i < ii; i++) { + newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; + } + return newres; + } + }, + findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t; + return { + x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, + y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y + }; + }, + curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { + var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x), + b = 2 * (c1x - p1x) - 2 * (c2x - c1x), + c = p1x - c1x, + t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a, + t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a, + y = [p1y, p2y], + x = [p1x, p2x], + dot; + abs(t1) > "1e12" && (t1 = .5); + abs(t2) > "1e12" && (t2 = .5); + if (t1 > 0 && t1 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); + x.push(dot.x); + y.push(dot.y); + } + if (t2 > 0 && t2 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); + x.push(dot.x); + y.push(dot.y); + } + a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y); + b = 2 * (c1y - p1y) - 2 * (c2y - c1y); + c = p1y - c1y; + t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a; + t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a; + abs(t1) > "1e12" && (t1 = .5); + abs(t2) > "1e12" && (t2 = .5); + if (t1 > 0 && t1 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); + x.push(dot.x); + y.push(dot.y); + } + if (t2 > 0 && t2 < 1) { + dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); + x.push(dot.x); + y.push(dot.y); + } + return { + min: {x: mmin[apply](0, x), y: mmin[apply](0, y)}, + max: {x: mmax[apply](0, x), y: mmax[apply](0, y)} + }; + }), + path2curve = R._path2curve = cacher(function (path, path2) { + var p = pathToAbsolute(path), + p2 = path2 && pathToAbsolute(path2), + attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, + processPath = function (path, d) { + var nx, ny; + if (!path) { + return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; + } + !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null); + switch (path[0]) { + case "M": + d.X = path[1]; + d.Y = path[2]; + break; + case "A": + path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1)))); + break; + case "S": + nx = d.x + (d.x - (d.bx || d.x)); + ny = d.y + (d.y - (d.by || d.y)); + path = ["C", nx, ny][concat](path.slice(1)); + break; + case "T": + d.qx = d.x + (d.x - (d.qx || d.x)); + d.qy = d.y + (d.y - (d.qy || d.y)); + path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); + break; + case "Q": + d.qx = path[1]; + d.qy = path[2]; + path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4])); + break; + case "L": + path = ["C"][concat](l2c(d.x, d.y, path[1], path[2])); + break; + case "H": + path = ["C"][concat](l2c(d.x, d.y, path[1], d.y)); + break; + case "V": + path = ["C"][concat](l2c(d.x, d.y, d.x, path[1])); + break; + case "Z": + path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y)); + break; + } + return path; + }, + fixArc = function (pp, i) { + if (pp[i].length > 7) { + pp[i].shift(); + var pi = pp[i]; + while (pi.length) { + pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6))); + } + pp.splice(i, 1); + ii = mmax(p.length, p2 && p2.length || 0); + } + }, + fixM = function (path1, path2, a1, a2, i) { + if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { + path2.splice(i, 0, ["M", a2.x, a2.y]); + a1.bx = 0; + a1.by = 0; + a1.x = path1[i][1]; + a1.y = path1[i][2]; + ii = mmax(p.length, p2 && p2.length || 0); + } + }; + for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) { + p[i] = processPath(p[i], attrs); + fixArc(p, i); + p2 && (p2[i] = processPath(p2[i], attrs2)); + p2 && fixArc(p2, i); + fixM(p, p2, attrs, attrs2, i); + fixM(p2, p, attrs2, attrs, i); + var seg = p[i], + seg2 = p2 && p2[i], + seglen = seg.length, + seg2len = p2 && seg2.length; + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; + attrs.by = toFloat(seg[seglen - 3]) || attrs.y; + attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); + attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); + attrs2.x = p2 && seg2[seg2len - 2]; + attrs2.y = p2 && seg2[seg2len - 1]; + } + return p2 ? [p, p2] : p; + }, null, pathClone), + parseDots = R._parseDots = cacher(function (gradient) { + var dots = []; + for (var i = 0, ii = gradient.length; i < ii; i++) { + var dot = {}, + par = gradient[i].match(/^([^:]*):?([\d\.]*)/); + dot.color = R.getRGB(par[1]); + if (dot.color.error) { + return null; + } + dot.color = dot.color.hex; + par[2] && (dot.offset = par[2] + "%"); + dots.push(dot); + } + for (i = 1, ii = dots.length - 1; i < ii; i++) { + if (!dots[i].offset) { + var start = toFloat(dots[i - 1].offset || 0), + end = 0; + for (var j = i + 1; j < ii; j++) { + if (dots[j].offset) { + end = dots[j].offset; + break; + } + } + if (!end) { + end = 100; + j = ii; + } + end = toFloat(end); + var d = (end - start) / (j - i + 1); + for (; i < j; i++) { + start += d; + dots[i].offset = start + "%"; + } + } + } + return dots; + }), + tear = R._tear = function (el, paper) { + el == paper.top && (paper.top = el.prev); + el == paper.bottom && (paper.bottom = el.next); + el.next && (el.next.prev = el.prev); + el.prev && (el.prev.next = el.next); + }, + tofront = R._tofront = function (el, paper) { + if (paper.top === el) { + return; + } + tear(el, paper); + el.next = null; + el.prev = paper.top; + paper.top.next = el; + paper.top = el; + }, + toback = R._toback = function (el, paper) { + if (paper.bottom === el) { + return; + } + tear(el, paper); + el.next = paper.bottom; + el.prev = null; + paper.bottom.prev = el; + paper.bottom = el; + }, + insertafter = R._insertafter = function (el, el2, paper) { + tear(el, paper); + el2 == paper.top && (paper.top = el); + el2.next && (el2.next.prev = el); + el.next = el2.next; + el.prev = el2; + el2.next = el; + }, + insertbefore = R._insertbefore = function (el, el2, paper) { + tear(el, paper); + el2 == paper.bottom && (paper.bottom = el); + el2.prev && (el2.prev.next = el); + el.prev = el2.prev; + el2.prev = el; + el.next = el2; + }, + removed = function (methodname) { + return function () { + throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object"); + }; + }, + extractTransform = R._extractTransform = function (el, tstr) { + if (tstr == null) { + return el._.transform; + } + tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E); + var tdata = R.parseTransformString(tstr), + deg = 0, + dx = 0, + dy = 0, + sx = 1, + sy = 1, + _ = el._, + m = new Matrix; + _.transform = tdata || []; + if (tdata) { + for (var i = 0, ii = tdata.length; i < ii; i++) { + var t = tdata[i], + tlen = t.length, + bb; + t[0] = Str(t[0]).toLowerCase(); + if (t[0] == "t" && tlen == 3) { + m.translate(t[1], t[2]); + } else if (t[0] == "r") { + if (tlen == 2) { + bb = bb || el.getBBox(1); + m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2); + deg += t[1]; + } else if (tlen == 4) { + m.rotate(t[1], t[2], t[3]); + deg += t[1]; + } + } else if (t[0] == "s") { + if (tlen == 2 || tlen == 3) { + bb = bb || el.getBBox(1); + m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2); + sx *= t[1]; + sy *= t[tlen - 1]; + } else if (tlen == 5) { + m.scale(t[1], t[2], t[3], t[4]); + sx *= t[1]; + sy *= t[2]; + } + } else if (t[0] == "m" && tlen == 7) { + m.add(t[1], t[2], t[3], t[4], t[5], t[6]); + } + _.dirtyT = 1; + el.matrix = m; + } + } + + el.matrix = m; + + _.sx = sx; + _.sy = sy; + _.deg = deg; + _.dx = dx = m.e; + _.dy = dy = m.f; + + if (sx == 1 && sy == 1 && !deg && _.bbox) { + _.bbox.x += +dx; + _.bbox.y += +dy; + } else { + _.dirtyT = 1; + } + }, + getEmpty = function (item) { + switch (item[0]) { + case "t": return ["t", 0, 0]; + case "m": return ["m", 1, 0, 0, 1, 0, 0]; + case "r": if (item.length == 4) { + return ["r", 0, item[2], item[3]]; + } else { + return ["r", 0]; + } + case "s": if (item.length == 5) { + return ["s", 1, 1, item[3], item[4]]; + } else if (item.length == 3) { + return ["s", 1, 1]; + } else { + return ["s", 1]; + } + } + }, + equaliseTransform = R._equaliseTransform = function (t1, t2) { + t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); + t1 = R.parseTransformString(t1) || []; + t2 = R.parseTransformString(t2) || []; + var maxlength = mmax(t1.length, t2.length), + from = [], + to = [], + i = 0, j, jj, + tt1, tt2; + for (; i < maxlength; i++) { + tt1 = t1[i] || getEmpty(t2[i]); + tt2 = t2[i] || getEmpty(tt1); + if ((tt1[0] != tt2[0]) || + (tt1[0] == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || + (tt1[0] == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) + ) { + return; + } + from[i] = []; + to[i] = []; + for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) { + j in tt1 && (from[i][j] = tt1[j]); + j in tt2 && (to[i][j] = tt2[j]); + } + } + return { + from: from, + to: to + }; + }; + R._getContainer = function (x, y, w, h) { + var container; + container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x; + if (container == null) { + return; + } + if (container.tagName) { + if (y == null) { + return { + container: container, + width: container.style.pixelWidth || container.offsetWidth, + height: container.style.pixelHeight || container.offsetHeight + }; + } else { + return { + container: container, + width: y, + height: w + }; + } + } + return { + container: 1, + x: x, + y: y, + width: w, + height: h + }; + }; + + R.pathToRelative = pathToRelative; + R._engine = {}; + + R.path2curve = path2curve; + + R.matrix = function (a, b, c, d, e, f) { + return new Matrix(a, b, c, d, e, f); + }; + function Matrix(a, b, c, d, e, f) { + if (a != null) { + this.a = +a; + this.b = +b; + this.c = +c; + this.d = +d; + this.e = +e; + this.f = +f; + } else { + this.a = 1; + this.b = 0; + this.c = 0; + this.d = 1; + this.e = 0; + this.f = 0; + } + } + (function (matrixproto) { + + matrixproto.add = function (a, b, c, d, e, f) { + var out = [[], [], []], + m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], + matrix = [[a, c, e], [b, d, f], [0, 0, 1]], + x, y, z, res; + + if (a && a instanceof Matrix) { + matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]; + } + + for (x = 0; x < 3; x++) { + for (y = 0; y < 3; y++) { + res = 0; + for (z = 0; z < 3; z++) { + res += m[x][z] * matrix[z][y]; + } + out[x][y] = res; + } + } + this.a = out[0][0]; + this.b = out[1][0]; + this.c = out[0][1]; + this.d = out[1][1]; + this.e = out[0][2]; + this.f = out[1][2]; + }; + + matrixproto.invert = function () { + var me = this, + x = me.a * me.d - me.b * me.c; + return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x); + }; + + matrixproto.clone = function () { + return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); + }; + + matrixproto.translate = function (x, y) { + this.add(1, 0, 0, 1, x, y); + }; + + matrixproto.scale = function (x, y, cx, cy) { + y == null && (y = x); + (cx || cy) && this.add(1, 0, 0, 1, cx, cy); + this.add(x, 0, 0, y, 0, 0); + (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy); + }; + + matrixproto.rotate = function (a, x, y) { + a = R.rad(a); + x = x || 0; + y = y || 0; + var cos = +math.cos(a).toFixed(9), + sin = +math.sin(a).toFixed(9); + this.add(cos, sin, -sin, cos, x, y); + this.add(1, 0, 0, 1, -x, -y); + }; + + matrixproto.x = function (x, y) { + return x * this.a + y * this.c + this.e; + }; + + matrixproto.y = function (x, y) { + return x * this.b + y * this.d + this.f; + }; + matrixproto.get = function (i) { + return +this[Str.fromCharCode(97 + i)].toFixed(4); + }; + matrixproto.toString = function () { + return R.svg ? + "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" : + [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join(); + }; + matrixproto.toFilter = function () { + return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) + + ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) + + ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')"; + }; + matrixproto.offset = function () { + return [this.e.toFixed(4), this.f.toFixed(4)]; + }; + function norm(a) { + return a[0] * a[0] + a[1] * a[1]; + } + function normalize(a) { + var mag = math.sqrt(norm(a)); + a[0] && (a[0] /= mag); + a[1] && (a[1] /= mag); + } + + matrixproto.split = function () { + var out = {}; + // translation + out.dx = this.e; + out.dy = this.f; + + // scale and shear + var row = [[this.a, this.c], [this.b, this.d]]; + out.scalex = math.sqrt(norm(row[0])); + normalize(row[0]); + + out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1]; + row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear]; + + out.scaley = math.sqrt(norm(row[1])); + normalize(row[1]); + out.shear /= out.scaley; + + // rotation + var sin = -row[0][1], + cos = row[1][1]; + if (cos < 0) { + out.rotate = R.deg(math.acos(cos)); + if (sin < 0) { + out.rotate = 360 - out.rotate; + } + } else { + out.rotate = R.deg(math.asin(sin)); + } + + out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate); + out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate; + out.noRotation = !+out.shear.toFixed(9) && !out.rotate; + return out; + }; + + matrixproto.toTransformString = function () { + var s = this.split(); + if (s.isSimple) { + return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0]; + } else { + return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)]; + } + }; + })(Matrix.prototype); + + // WebKit rendering bug workaround method + var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/); + if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") || + (navigator.vendor == "Google Inc." && version && version[1] < 8)) { + + paperproto.safari = function () { + var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"}); + setTimeout(function () {rect.remove();}); + }; + } else { + paperproto.safari = fun; + } + + var preventDefault = function () { + this.returnValue = false; + }, + preventTouch = function () { + return this.originalEvent.preventDefault(); + }, + stopPropagation = function () { + this.cancelBubble = true; + }, + stopTouch = function () { + return this.originalEvent.stopPropagation(); + }, + addEvent = (function () { + if (g.doc.addEventListener) { + return function (obj, type, fn, element) { + var realName = supportsTouch && touchMap[type] ? touchMap[type] : type, + f = function (e) { + var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop, + scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft, + x = e.clientX + scrollX, + y = e.clientY + scrollY; + if (supportsTouch && touchMap[has](type)) { + for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) { + if (e.targetTouches[i].target == obj) { + var olde = e; + e = e.targetTouches[i]; + e.originalEvent = olde; + e.preventDefault = preventTouch; + e.stopPropagation = stopTouch; + break; + } + } + } + return fn.call(element, e, x, y); + }; + obj.addEventListener(realName, f, false); + return function () { + obj.removeEventListener(realName, f, false); + return true; + }; + }; + } else if (g.doc.attachEvent) { + return function (obj, type, fn, element) { + var f = function (e) { + e = e || g.win.event; + var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop, + scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft, + x = e.clientX + scrollX, + y = e.clientY + scrollY; + e.preventDefault = e.preventDefault || preventDefault; + e.stopPropagation = e.stopPropagation || stopPropagation; + return fn.call(element, e, x, y); + }; + obj.attachEvent("on" + type, f); + var detacher = function () { + obj.detachEvent("on" + type, f); + return true; + }; + return detacher; + }; + } + })(), + drag = [], + dragMove = function (e) { + var x = e.clientX, + y = e.clientY, + scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop, + scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft, + dragi, + j = drag.length; + while (j--) { + dragi = drag[j]; + if (supportsTouch) { + var i = e.touches.length, + touch; + while (i--) { + touch = e.touches[i]; + if (touch.identifier == dragi.el._drag.id) { + x = touch.clientX; + y = touch.clientY; + (e.originalEvent ? e.originalEvent : e).preventDefault(); + break; + } + } + } else { + e.preventDefault(); + } + var node = dragi.el.node, + o, + next = node.nextSibling, + parent = node.parentNode, + display = node.style.display; + g.win.opera && parent.removeChild(node); + node.style.display = "none"; + o = dragi.el.paper.getElementByPoint(x, y); + node.style.display = display; + g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node)); + o && eve("drag.over." + dragi.el.id, dragi.el, o); + x += scrollX; + y += scrollY; + eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e); + } + }, + dragUp = function (e) { + R.unmousemove(dragMove).unmouseup(dragUp); + var i = drag.length, + dragi; + while (i--) { + dragi = drag[i]; + dragi.el._drag = {}; + eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e); + } + drag = []; + }, + + elproto = R.el = {}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for (var i = events.length; i--;) { + (function (eventName) { + R[eventName] = elproto[eventName] = function (fn, scope) { + if (R.is(fn, "function")) { + this.events = this.events || []; + this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)}); + } + return this; + }; + R["un" + eventName] = elproto["un" + eventName] = function (fn) { + var events = this.events, + l = events.length; + while (l--) if (events[l].name == eventName && events[l].f == fn) { + events[l].unbind(); + events.splice(l, 1); + !events.length && delete this.events; + return this; + } + return this; + }; + })(events[i]); + } + + + elproto.data = function (key, value) { + var data = eldata[this.id] = eldata[this.id] || {}; + if (arguments.length == 1) { + if (R.is(key, "object")) { + for (var i in key) if (key[has](i)) { + this.data(i, key[i]); + } + return this; + } + eve("data.get." + this.id, this, data[key], key); + return data[key]; + } + data[key] = value; + eve("data.set." + this.id, this, value, key); + return this; + }; + + elproto.removeData = function (key) { + if (key == null) { + eldata[this.id] = {}; + } else { + eldata[this.id] && delete eldata[this.id][key]; + } + return this; + }; + + elproto.hover = function (f_in, f_out, scope_in, scope_out) { + return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in); + }; + + elproto.unhover = function (f_in, f_out) { + return this.unmouseover(f_in).unmouseout(f_out); + }; + + elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) { + function start(e) { + (e.originalEvent || e).preventDefault(); + var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop, + scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft; + this._drag.x = e.clientX + scrollX; + this._drag.y = e.clientY + scrollY; + this._drag.id = e.identifier; + !drag.length && R.mousemove(dragMove).mouseup(dragUp); + drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope}); + onstart && eve.on("drag.start." + this.id, onstart); + onmove && eve.on("drag.move." + this.id, onmove); + onend && eve.on("drag.end." + this.id, onend); + eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e); + } + this._drag = {}; + this.mousedown(start); + return this; + }; + + elproto.onDragOver = function (f) { + f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id); + }; + + elproto.undrag = function () { + var i = drag.length; + while (i--) if (drag[i].el == this) { + R.unmousedown(drag[i].start); + drag.splice(i++, 1); + eve.unbind("drag.*." + this.id); + } + !drag.length && R.unmousemove(dragMove).unmouseup(dragUp); + }; + + paperproto.circle = function (x, y, r) { + var out = R._engine.circle(this, x || 0, y || 0, r || 0); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.rect = function (x, y, w, h, r) { + var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.ellipse = function (x, y, rx, ry) { + var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.path = function (pathString) { + pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E); + var out = R._engine.path(R.format[apply](R, arguments), this); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.image = function (src, x, y, w, h) { + var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.text = function (x, y, text) { + var out = R._engine.text(this, x || 0, y || 0, Str(text)); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.set = function (itemsArray) { + !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length)); + var out = new Set(itemsArray); + this.__set__ && this.__set__.push(out); + return out; + }; + + paperproto.setStart = function (set) { + this.__set__ = set || this.set(); + }; + + paperproto.setFinish = function (set) { + var out = this.__set__; + delete this.__set__; + return out; + }; + + paperproto.setSize = function (width, height) { + return R._engine.setSize.call(this, width, height); + }; + + paperproto.setViewBox = function (x, y, w, h, fit) { + return R._engine.setViewBox.call(this, x, y, w, h, fit); + }; + + + paperproto.top = paperproto.bottom = null; + + paperproto.raphael = R; + var getOffset = function (elem) { + var box = elem.getBoundingClientRect(), + doc = elem.ownerDocument, + body = doc.body, + docElem = doc.documentElement, + clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, + top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop, + left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft; + return { + y: top, + x: left + }; + }; + + paperproto.getElementByPoint = function (x, y) { + var paper = this, + svg = paper.canvas, + target = g.doc.elementFromPoint(x, y); + if (g.win.opera && target.tagName == "svg") { + var so = getOffset(svg), + sr = svg.createSVGRect(); + sr.x = x - so.x; + sr.y = y - so.y; + sr.width = sr.height = 1; + var hits = svg.getIntersectionList(sr, null); + if (hits.length) { + target = hits[hits.length - 1]; + } + } + if (!target) { + return null; + } + while (target.parentNode && target != svg.parentNode && !target.raphael) { + target = target.parentNode; + } + target == paper.canvas.parentNode && (target = svg); + target = target && target.raphael ? paper.getById(target.raphaelid) : null; + return target; + }; + + paperproto.getById = function (id) { + var bot = this.bottom; + while (bot) { + if (bot.id == id) { + return bot; + } + bot = bot.next; + } + return null; + }; + + paperproto.forEach = function (callback, thisArg) { + var bot = this.bottom; + while (bot) { + if (callback.call(thisArg, bot) === false) { + return this; + } + bot = bot.next; + } + return this; + }; + function x_y() { + return this.x + S + this.y; + } + function x_y_w_h() { + return this.x + S + this.y + S + this.width + " \xd7 " + this.height; + } + + elproto.getBBox = function (isWithoutTransform) { + if (this.removed) { + return {}; + } + var _ = this._; + if (isWithoutTransform) { + if (_.dirty || !_.bboxwt) { + this.realPath = getPath[this.type](this); + _.bboxwt = pathDimensions(this.realPath); + _.bboxwt.toString = x_y_w_h; + _.dirty = 0; + } + return _.bboxwt; + } + if (_.dirty || _.dirtyT || !_.bbox) { + if (_.dirty || !this.realPath) { + _.bboxwt = 0; + this.realPath = getPath[this.type](this); + } + _.bbox = pathDimensions(mapPath(this.realPath, this.matrix)); + _.bbox.toString = x_y_w_h; + _.dirty = _.dirtyT = 0; + } + return _.bbox; + }; + + elproto.clone = function () { + if (this.removed) { + return null; + } + return this.paper[this.type]().attr(this.attr()); + }; + + elproto.glow = function (glow) { + if (this.type == "text") { + return null; + } + glow = glow || {}; + var s = { + width: (glow.width || 10) + (+this.attr("stroke-width") || 1), + fill: glow.fill || false, + opacity: glow.opacity || .5, + offsetx: glow.offsetx || 0, + offsety: glow.offsety || 0, + color: glow.color || "#000" + }, + c = s.width / 2, + r = this.paper, + out = r.set(), + path = this.realPath || getPath[this.type](this); + path = this.matrix ? mapPath(path, this.matrix) : path; + for (var i = 1; i < c + 1; i++) { + out.push(r.path(path).attr({ + stroke: s.color, + fill: s.fill ? s.color : "none", + "stroke-linejoin": "round", + "stroke-linecap": "round", + "stroke-width": +(s.width / c * i).toFixed(3), + opacity: +(s.opacity / c).toFixed(3) + })); + } + return out.insertBefore(this).translate(s.offsetx, s.offsety); + }; + var curveslengths = {}, + getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { + var len = 0, + precision = 100, + name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(), + cache = curveslengths[name], + old, dot; + !cache && (curveslengths[name] = cache = {data: []}); + cache.timer && clearTimeout(cache.timer); + cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3); + if (length != null && !cache.precision) { + var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y); + cache.precision = ~~total * 10; + cache.data = []; + } + precision = cache.precision || precision; + for (var i = 0; i < precision + 1; i++) { + if (cache.data[i * precision]) { + dot = cache.data[i * precision]; + } else { + dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision); + cache.data[i * precision] = dot; + } + i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5)); + if (length != null && len >= length) { + return dot; + } + old = dot; + } + if (length == null) { + return len; + } + }, + getLengthFactory = function (istotal, subpath) { + return function (path, length, onlystart) { + path = path2curve(path); + var x, y, p, l, sp = "", subpaths = {}, point, + len = 0; + for (var i = 0, ii = path.length; i < ii; i++) { + p = path[i]; + if (p[0] == "M") { + x = +p[1]; + y = +p[2]; + } else { + l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); + if (len + l > length) { + if (subpath && !subpaths.start) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); + sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y]; + if (onlystart) {return sp;} + subpaths.start = sp; + sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join(); + len += l; + x = +p[5]; + y = +p[6]; + continue; + } + if (!istotal && !subpath) { + point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); + return {x: point.x, y: point.y, alpha: point.alpha}; + } + } + len += l; + x = +p[5]; + y = +p[6]; + } + sp += p.shift() + p; + } + subpaths.end = sp; + point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); + point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha}); + return point; + }; + }; + var getTotalLength = getLengthFactory(1), + getPointAtLength = getLengthFactory(), + getSubpathsAtLength = getLengthFactory(0, 1); + + R.getTotalLength = getTotalLength; + + R.getPointAtLength = getPointAtLength; + + R.getSubpath = function (path, from, to) { + if (this.getTotalLength(path) - to < 1e-6) { + return getSubpathsAtLength(path, from).end; + } + var a = getSubpathsAtLength(path, to, 1); + return from ? getSubpathsAtLength(a, from).end : a; + }; + + elproto.getTotalLength = function () { + if (this.type != "path") {return;} + if (this.node.getTotalLength) { + return this.node.getTotalLength(); + } + return getTotalLength(this.attrs.path); + }; + + elproto.getPointAtLength = function (length) { + if (this.type != "path") {return;} + return getPointAtLength(this.attrs.path, length); + }; + + elproto.getSubpath = function (from, to) { + if (this.type != "path") {return;} + return R.getSubpath(this.attrs.path, from, to); + }; + + var ef = R.easing_formulas = { + linear: function (n) { + return n; + }, + "<": function (n) { + return pow(n, 1.7); + }, + ">": function (n) { + return pow(n, .48); + }, + "<>": function (n) { + var q = .48 - n / 1.04, + Q = math.sqrt(.1734 + q * q), + x = Q - q, + X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1), + y = -Q - q, + Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1), + t = X + Y + .5; + return (1 - t) * 3 * t * t + t * t * t; + }, + backIn: function (n) { + var s = 1.70158; + return n * n * ((s + 1) * n - s); + }, + backOut: function (n) { + n = n - 1; + var s = 1.70158; + return n * n * ((s + 1) * n + s) + 1; + }, + elastic: function (n) { + if (n == !!n) { + return n; + } + return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1; + }, + bounce: function (n) { + var s = 7.5625, + p = 2.75, + l; + if (n < (1 / p)) { + l = s * n * n; + } else { + if (n < (2 / p)) { + n -= (1.5 / p); + l = s * n * n + .75; + } else { + if (n < (2.5 / p)) { + n -= (2.25 / p); + l = s * n * n + .9375; + } else { + n -= (2.625 / p); + l = s * n * n + .984375; + } + } + } + return l; + } + }; + ef.easeIn = ef["ease-in"] = ef["<"]; + ef.easeOut = ef["ease-out"] = ef[">"]; + ef.easeInOut = ef["ease-in-out"] = ef["<>"]; + ef["back-in"] = ef.backIn; + ef["back-out"] = ef.backOut; + + var animationElements = [], + requestAnimFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + setTimeout(callback, 16); + }, + animation = function () { + var Now = +new Date, + l = 0; + for (; l < animationElements.length; l++) { + var e = animationElements[l]; + if (e.el.removed || e.paused) { + continue; + } + var time = Now - e.start, + ms = e.ms, + easing = e.easing, + from = e.from, + diff = e.diff, + to = e.to, + t = e.t, + that = e.el, + set = {}, + now; + if (e.initstatus) { + time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms; + e.status = e.initstatus; + delete e.initstatus; + e.stop && animationElements.splice(l--, 1); + } else { + e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top; + } + if (time < 0) { + continue; + } + if (time < ms) { + var pos = easing(time / ms); + for (var attr in from) if (from[has](attr)) { + switch (availableAnimAttrs[attr]) { + case nu: + now = +from[attr] + pos * ms * diff[attr]; + break; + case "colour": + now = "rgb(" + [ + upto255(round(from[attr].r + pos * ms * diff[attr].r)), + upto255(round(from[attr].g + pos * ms * diff[attr].g)), + upto255(round(from[attr].b + pos * ms * diff[attr].b)) + ].join(",") + ")"; + break; + case "path": + now = []; + for (var i = 0, ii = from[attr].length; i < ii; i++) { + now[i] = [from[attr][i][0]]; + for (var j = 1, jj = from[attr][i].length; j < jj; j++) { + now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j]; + } + now[i] = now[i].join(S); + } + now = now.join(S); + break; + case "transform": + if (diff[attr].real) { + now = []; + for (i = 0, ii = from[attr].length; i < ii; i++) { + now[i] = [from[attr][i][0]]; + for (j = 1, jj = from[attr][i].length; j < jj; j++) { + now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j]; + } + } + } else { + var get = function (i) { + return +from[attr][i] + pos * ms * diff[attr][i]; + }; + // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]]; + now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]]; + } + break; + case "csv": + if (attr == "clip-rect") { + now = []; + i = 4; + while (i--) { + now[i] = +from[attr][i] + pos * ms * diff[attr][i]; + } + } + break; + default: + var from2 = [].concat(from[attr]); + now = []; + i = that.paper.customAttributes[attr].length; + while (i--) { + now[i] = +from2[i] + pos * ms * diff[attr][i]; + } + break; + } + set[attr] = now; + } + that.attr(set); + (function (id, that, anim) { + setTimeout(function () { + eve("anim.frame." + id, that, anim); + }); + })(that.id, that, e.anim); + } else { + (function(f, el, a) { + setTimeout(function() { + eve("anim.frame." + el.id, el, a); + eve("anim.finish." + el.id, el, a); + R.is(f, "function") && f.call(el); + }); + })(e.callback, that, e.anim); + that.attr(to); + animationElements.splice(l--, 1); + if (e.repeat > 1 && !e.next) { + runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1); + } + if (e.next && !e.stop) { + runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat); + } + } + } + R.svg && that && that.paper && that.paper.safari(); + animationElements.length && requestAnimFrame(animation); + }, + upto255 = function (color) { + return color > 255 ? 255 : color < 0 ? 0 : color; + }; + + elproto.animateWith = function (element, anim, params, ms, easing, callback) { + var a = params ? R.animation(params, ms, easing, callback) : anim; + status = element.status(anim); + return this.animate(a).status(a, status * anim.ms / a.ms); + }; + function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) { + var cx = 3 * p1x, + bx = 3 * (p2x - p1x) - cx, + ax = 1 - cx - bx, + cy = 3 * p1y, + by = 3 * (p2y - p1y) - cy, + ay = 1 - cy - by; + function sampleCurveX(t) { + return ((ax * t + bx) * t + cx) * t; + } + function solve(x, epsilon) { + var t = solveCurveX(x, epsilon); + return ((ay * t + by) * t + cy) * t; + } + function solveCurveX(x, epsilon) { + var t0, t1, t2, x2, d2, i; + for(t2 = x, i = 0; i < 8; i++) { + x2 = sampleCurveX(t2) - x; + if (abs(x2) < epsilon) { + return t2; + } + d2 = (3 * ax * t2 + 2 * bx) * t2 + cx; + if (abs(d2) < 1e-6) { + break; + } + t2 = t2 - x2 / d2; + } + t0 = 0; + t1 = 1; + t2 = x; + if (t2 < t0) { + return t0; + } + if (t2 > t1) { + return t1; + } + while (t0 < t1) { + x2 = sampleCurveX(t2); + if (abs(x2 - x) < epsilon) { + return t2; + } + if (x > x2) { + t0 = t2; + } else { + t1 = t2; + } + t2 = (t1 - t0) / 2 + t0; + } + return t2; + } + return solve(t, 1 / (200 * duration)); + } + elproto.onAnimation = function (f) { + f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id); + return this; + }; + function Animation(anim, ms) { + var percents = [], + newAnim = {}; + this.ms = ms; + this.times = 1; + if (anim) { + for (var attr in anim) if (anim[has](attr)) { + newAnim[toFloat(attr)] = anim[attr]; + percents.push(toFloat(attr)); + } + percents.sort(sortByNumber); + } + this.anim = newAnim; + this.top = percents[percents.length - 1]; + this.percents = percents; + } + + Animation.prototype.delay = function (delay) { + var a = new Animation(this.anim, this.ms); + a.times = this.times; + a.del = +delay || 0; + return a; + }; + + Animation.prototype.repeat = function (times) { + var a = new Animation(this.anim, this.ms); + a.del = this.del; + a.times = math.floor(mmax(times, 0)) || 1; + return a; + }; + function runAnimation(anim, element, percent, status, totalOrigin, times) { + percent = toFloat(percent); + var params, + isInAnim, + isInAnimSet, + percents = [], + next, + prev, + timestamp, + ms = anim.ms, + from = {}, + to = {}, + diff = {}; + if (status) { + for (i = 0, ii = animationElements.length; i < ii; i++) { + var e = animationElements[i]; + if (e.el.id == element.id && e.anim == anim) { + if (e.percent != percent) { + animationElements.splice(i, 1); + isInAnimSet = 1; + } else { + isInAnim = e; + } + element.attr(e.totalOrigin); + break; + } + } + } else { + status = +to; // NaN + } + for (var i = 0, ii = anim.percents.length; i < ii; i++) { + if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) { + percent = anim.percents[i]; + prev = anim.percents[i - 1] || 0; + ms = ms / anim.top * (percent - prev); + next = anim.percents[i + 1]; + params = anim.anim[percent]; + break; + } else if (status) { + element.attr(anim.anim[anim.percents[i]]); + } + } + if (!params) { + return; + } + if (!isInAnim) { + for (attr in params) if (params[has](attr)) { + if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) { + from[attr] = element.attr(attr); + (from[attr] == null) && (from[attr] = availableAttrs[attr]); + to[attr] = params[attr]; + switch (availableAnimAttrs[attr]) { + case nu: + diff[attr] = (to[attr] - from[attr]) / ms; + break; + case "colour": + from[attr] = R.getRGB(from[attr]); + var toColour = R.getRGB(to[attr]); + diff[attr] = { + r: (toColour.r - from[attr].r) / ms, + g: (toColour.g - from[attr].g) / ms, + b: (toColour.b - from[attr].b) / ms + }; + break; + case "path": + var pathes = path2curve(from[attr], to[attr]), + toPath = pathes[1]; + from[attr] = pathes[0]; + diff[attr] = []; + for (i = 0, ii = from[attr].length; i < ii; i++) { + diff[attr][i] = [0]; + for (var j = 1, jj = from[attr][i].length; j < jj; j++) { + diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms; + } + } + break; + case "transform": + var _ = element._, + eq = equaliseTransform(_[attr], to[attr]); + if (eq) { + from[attr] = eq.from; + to[attr] = eq.to; + diff[attr] = []; + diff[attr].real = true; + for (i = 0, ii = from[attr].length; i < ii; i++) { + diff[attr][i] = [from[attr][i][0]]; + for (j = 1, jj = from[attr][i].length; j < jj; j++) { + diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms; + } + } + } else { + var m = (element.matrix || new Matrix), + to2 = { + _: {transform: _.transform}, + getBBox: function () { + return element.getBBox(1); + } + }; + from[attr] = [ + m.a, + m.b, + m.c, + m.d, + m.e, + m.f + ]; + extractTransform(to2, to[attr]); + to[attr] = to2._.transform; + diff[attr] = [ + (to2.matrix.a - m.a) / ms, + (to2.matrix.b - m.b) / ms, + (to2.matrix.c - m.c) / ms, + (to2.matrix.d - m.d) / ms, + (to2.matrix.e - m.e) / ms, + (to2.matrix.e - m.f) / ms + ]; + // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy]; + // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }}; + // extractTransform(to2, to[attr]); + // diff[attr] = [ + // (to2._.sx - _.sx) / ms, + // (to2._.sy - _.sy) / ms, + // (to2._.deg - _.deg) / ms, + // (to2._.dx - _.dx) / ms, + // (to2._.dy - _.dy) / ms + // ]; + } + break; + case "csv": + var values = Str(params[attr]).split(separator), + from2 = Str(from[attr]).split(separator); + if (attr == "clip-rect") { + from[attr] = from2; + diff[attr] = []; + i = from2.length; + while (i--) { + diff[attr][i] = (values[i] - from[attr][i]) / ms; + } + } + to[attr] = values; + break; + default: + values = [].concat(params[attr]); + from2 = [].concat(from[attr]); + diff[attr] = []; + i = element.paper.customAttributes[attr].length; + while (i--) { + diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms; + } + break; + } + } + } + var easing = params.easing, + easyeasy = R.easing_formulas[easing]; + if (!easyeasy) { + easyeasy = Str(easing).match(bezierrg); + if (easyeasy && easyeasy.length == 5) { + var curve = easyeasy; + easyeasy = function (t) { + return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms); + }; + } else { + easyeasy = pipe; + } + } + timestamp = params.start || anim.start || +new Date; + e = { + anim: anim, + percent: percent, + timestamp: timestamp, + start: timestamp + (anim.del || 0), + status: 0, + initstatus: status || 0, + stop: false, + ms: ms, + easing: easyeasy, + from: from, + diff: diff, + to: to, + el: element, + callback: params.callback, + prev: prev, + next: next, + repeat: times || anim.times, + origin: element.attr(), + totalOrigin: totalOrigin + }; + animationElements.push(e); + if (status && !isInAnim && !isInAnimSet) { + e.stop = true; + e.start = new Date - ms * status; + if (animationElements.length == 1) { + return animation(); + } + } + if (isInAnimSet) { + e.start = new Date - e.ms * status; + } + animationElements.length == 1 && requestAnimFrame(animation); + } else { + isInAnim.initstatus = status; + isInAnim.start = new Date - isInAnim.ms * status; + } + eve("anim.start." + element.id, element, anim); + } + + R.animation = function (params, ms, easing, callback) { + if (params instanceof Animation) { + return params; + } + if (R.is(easing, "function") || !easing) { + callback = callback || easing || null; + easing = null; + } + params = Object(params); + ms = +ms || 0; + var p = {}, + json, + attr; + for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) { + json = true; + p[attr] = params[attr]; + } + if (!json) { + return new Animation(params, ms); + } else { + easing && (p.easing = easing); + callback && (p.callback = callback); + return new Animation({100: p}, ms); + } + }; + + elproto.animate = function (params, ms, easing, callback) { + var element = this; + if (element.removed) { + callback && callback.call(element); + return element; + } + var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback); + runAnimation(anim, element, anim.percents[0], null, element.attr()); + return element; + }; + + elproto.setTime = function (anim, value) { + if (anim && value != null) { + this.status(anim, mmin(value, anim.ms) / anim.ms); + } + return this; + }; + + elproto.status = function (anim, value) { + var out = [], + i = 0, + len, + e; + if (value != null) { + runAnimation(anim, this, -1, mmin(value, 1)); + return this; + } else { + len = animationElements.length; + for (; i < len; i++) { + e = animationElements[i]; + if (e.el.id == this.id && (!anim || e.anim == anim)) { + if (anim) { + return e.status; + } + out.push({ + anim: e.anim, + status: e.status + }); + } + } + if (anim) { + return 0; + } + return out; + } + }; + + elproto.pause = function (anim) { + for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) { + if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) { + animationElements[i].paused = true; + } + } + return this; + }; + + elproto.resume = function (anim) { + for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) { + var e = animationElements[i]; + if (eve("anim.resume." + this.id, this, e.anim) !== false) { + delete e.paused; + this.status(e.anim, e.status); + } + } + return this; + }; + + elproto.stop = function (anim) { + for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) { + if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) { + animationElements.splice(i--, 1); + } + } + return this; + }; + elproto.toString = function () { + return "Rapha\xebl\u2019s object"; + }; + + // Set + var Set = function (items) { + this.items = []; + this.length = 0; + this.type = "set"; + if (items) { + for (var i = 0, ii = items.length; i < ii; i++) { + if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) { + this[this.items.length] = this.items[this.items.length] = items[i]; + this.length++; + } + } + } + }, + setproto = Set.prototype; + + setproto.push = function () { + var item, + len; + for (var i = 0, ii = arguments.length; i < ii; i++) { + item = arguments[i]; + if (item && (item.constructor == elproto.constructor || item.constructor == Set)) { + len = this.items.length; + this[len] = this.items[len] = item; + this.length++; + } + } + return this; + }; + + setproto.pop = function () { + this.length && delete this[this.length--]; + return this.items.pop(); + }; + + setproto.forEach = function (callback, thisArg) { + for (var i = 0, ii = this.items.length; i < ii; i++) { + if (callback.call(thisArg, this.items[i]) === false) { + return this; + } + } + return this; + }; + for (var method in elproto) if (elproto[has](method)) { + setproto[method] = (function (methodname) { + return function () { + var arg = arguments; + return this.forEach(function (el) { + el[methodname][apply](el, arg); + }); + }; + })(method); + } + setproto.attr = function (name, value) { + if (name && R.is(name, array) && R.is(name[0], "object")) { + for (var j = 0, jj = name.length; j < jj; j++) { + this.items[j].attr(name[j]); + } + } else { + for (var i = 0, ii = this.items.length; i < ii; i++) { + this.items[i].attr(name, value); + } + } + return this; + }; + + setproto.clear = function () { + while (this.length) { + this.pop(); + } + }; + + setproto.splice = function (index, count, insertion) { + index = index < 0 ? mmax(this.length + index, 0) : index; + count = mmax(0, mmin(this.length - index, count)); + var tail = [], + todel = [], + args = [], + i; + for (i = 2; i < arguments.length; i++) { + args.push(arguments[i]); + } + for (i = 0; i < count; i++) { + todel.push(this[index + i]); + } + for (; i < this.length - index; i++) { + tail.push(this[index + i]); + } + var arglen = args.length; + for (i = 0; i < arglen + tail.length; i++) { + this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen]; + } + i = this.items.length = this.length -= count - arglen; + while (this[i]) { + delete this[i++]; + } + return new Set(todel); + }; + + setproto.exclude = function (el) { + for (var i = 0, ii = this.length, found; i < ii; i++) if (found || this[i] == el) { + this[i] = this[i + 1]; + found = 1; + } + if (found) { + this.length--; + delete this[i]; + return true; + } + }; + setproto.animate = function (params, ms, easing, callback) { + (R.is(easing, "function") || !easing) && (callback = easing || null); + var len = this.items.length, + i = len, + item, + set = this, + collector; + if (!len) { + return this; + } + callback && (collector = function () { + !--len && callback.call(set); + }); + easing = R.is(easing, string) ? easing : collector; + var anim = R.animation(params, ms, easing, collector); + item = this.items[--i].animate(anim); + while (i--) { + this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim); + } + return this; + }; + setproto.insertAfter = function (el) { + var i = this.items.length; + while (i--) { + this.items[i].insertAfter(el); + } + return this; + }; + setproto.getBBox = function () { + var x = [], + y = [], + w = [], + h = []; + for (var i = this.items.length; i--;) if (!this.items[i].removed) { + var box = this.items[i].getBBox(); + x.push(box.x); + y.push(box.y); + w.push(box.x + box.width); + h.push(box.y + box.height); + } + x = mmin[apply](0, x); + y = mmin[apply](0, y); + return { + x: x, + y: y, + width: mmax[apply](0, w) - x, + height: mmax[apply](0, h) - y + }; + }; + setproto.clone = function (s) { + s = new Set; + for (var i = 0, ii = this.items.length; i < ii; i++) { + s.push(this.items[i].clone()); + } + return s; + }; + setproto.toString = function () { + return "Rapha\xebl\u2018s set"; + }; + + + R.registerFont = function (font) { + if (!font.face) { + return font; + } + this.fonts = this.fonts || {}; + var fontcopy = { + w: font.w, + face: {}, + glyphs: {} + }, + family = font.face["font-family"]; + for (var prop in font.face) if (font.face[has](prop)) { + fontcopy.face[prop] = font.face[prop]; + } + if (this.fonts[family]) { + this.fonts[family].push(fontcopy); + } else { + this.fonts[family] = [fontcopy]; + } + if (!font.svg) { + fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10); + for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) { + var path = font.glyphs[glyph]; + fontcopy.glyphs[glyph] = { + w: path.w, + k: {}, + d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) { + return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M"; + }) + "z" + }; + if (path.k) { + for (var k in path.k) if (path[has](k)) { + fontcopy.glyphs[glyph].k[k] = path.k[k]; + } + } + } + } + return font; + }; + + paperproto.getFont = function (family, weight, style, stretch) { + stretch = stretch || "normal"; + style = style || "normal"; + weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400; + if (!R.fonts) { + return; + } + var font = R.fonts[family]; + if (!font) { + var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i"); + for (var fontName in R.fonts) if (R.fonts[has](fontName)) { + if (name.test(fontName)) { + font = R.fonts[fontName]; + break; + } + } + } + var thefont; + if (font) { + for (var i = 0, ii = font.length; i < ii; i++) { + thefont = font[i]; + if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) { + break; + } + } + } + return thefont; + }; + + paperproto.print = function (x, y, string, font, size, origin, letter_spacing) { + origin = origin || "middle"; // baseline|middle + letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1); + var out = this.set(), + letters = Str(string).split(E), + shift = 0, + path = E, + scale; + R.is(font, string) && (font = this.getFont(font)); + if (font) { + scale = (size || 16) / font.face["units-per-em"]; + var bb = font.face.bbox.split(separator), + top = +bb[0], + height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2); + for (var i = 0, ii = letters.length; i < ii; i++) { + var prev = i && font.glyphs[letters[i - 1]] || {}, + curr = font.glyphs[letters[i]]; + shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0; + curr && curr.d && out.push(this.path(curr.d).attr({ + fill: "#000", + stroke: "none", + transform: [["t", shift * scale, 0]] + })); + } + out.transform(["...s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]); + } + return out; + }; + + + R.format = function (token, params) { + var args = R.is(params, array) ? [0][concat](params) : arguments; + token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) { + return args[++i] == null ? E : args[i]; + })); + return token || E; + }; + + R.fullfill = (function () { + var tokenRegex = /\{([^\}]+)\}/g, + objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties + replacer = function (all, key, obj) { + var res = obj; + key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) { + name = name || quotedName; + if (res) { + if (name in res) { + res = res[name]; + } + typeof res == "function" && isFunc && (res = res()); + } + }); + res = (res == null || res == obj ? all : res) + ""; + return res; + }; + return function (str, obj) { + return String(str).replace(tokenRegex, function (all, key) { + return replacer(all, key, obj); + }); + }; + })(); + + R.ninja = function () { + oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael; + return R; + }; + + R.st = setproto; + // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html + (function (doc, loaded, f) { + if (doc.readyState == null && doc.addEventListener){ + doc.addEventListener(loaded, f = function () { + doc.removeEventListener(loaded, f, false); + doc.readyState = "complete"; + }, false); + doc.readyState = "loading"; + } + function isLoaded() { + (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("DOMload"); + } + isLoaded(); + })(document, "DOMContentLoaded"); + + oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R); + + eve.on("DOMload", function () { + loaded = true; + }); +})(); + +// ┌─────────────────────────────────────────────────────────────────────┐ \\ +// │ Raphaël 2 - JavaScript Vector Library │ \\ +// ├─────────────────────────────────────────────────────────────────────┤ \\ +// │ SVG Module │ \\ +// ├─────────────────────────────────────────────────────────────────────┤ \\ +// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com) │ \\ +// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com) │ \\ +// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\ +// └─────────────────────────────────────────────────────────────────────┘ \\ +window.Raphael.svg && function (R) { + var has = "hasOwnProperty", + Str = String, + toFloat = parseFloat, + toInt = parseInt, + math = Math, + mmax = math.max, + abs = math.abs, + pow = math.pow, + separator = /[, ]+/, + eve = R.eve, + E = "", + S = " "; + var xlink = "http://www.w3.org/1999/xlink", + markers = { + block: "M5,0 0,2.5 5,5z", + classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z", + diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z", + open: "M6,1 1,3.5 6,6", + oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z" + }, + markerCounter = {}; + R.toString = function () { + return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version; + }; + var $ = function (el, attr) { + if (attr) { + if (typeof el == "string") { + el = $(el); + } + for (var key in attr) if (attr[has](key)) { + if (key.substring(0, 6) == "xlink:") { + el.setAttributeNS(xlink, key.substring(6), Str(attr[key])); + } else { + el.setAttribute(key, Str(attr[key])); + } + } + } else { + el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el); + el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)"); + } + return el; + }, + gradients = {}, + rgGrad = /^url\(#(.*)\)$/, + removeGradientFill = function (node, paper) { + var oid = node.getAttribute("fill"); + oid = oid && oid.match(rgGrad); + if (oid && !--gradients[oid[1]]) { + delete gradients[oid[1]]; + paper.defs.removeChild(R._g.doc.getElementById(oid[1])); + } + }, + addGradientFill = function (element, gradient) { + var type = "linear", + id = element.id + gradient, + fx = .5, fy = .5, + o = element.node, + SVG = element.paper, + s = o.style, + el = R._g.doc.getElementById(id); + if (!el) { + gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) { + type = "radial"; + if (_fx && _fy) { + fx = toFloat(_fx); + fy = toFloat(_fy); + var dir = ((fy > .5) * 2 - 1); + pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && + (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) && + fy != .5 && + (fy = fy.toFixed(5) - 1e-5 * dir); + } + return E; + }); + gradient = gradient.split(/\s*\-\s*/); + if (type == "linear") { + var angle = gradient.shift(); + angle = -toFloat(angle); + if (isNaN(angle)) { + return null; + } + var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))], + max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1); + vector[2] *= max; + vector[3] *= max; + if (vector[2] < 0) { + vector[0] = -vector[2]; + vector[2] = 0; + } + if (vector[3] < 0) { + vector[1] = -vector[3]; + vector[3] = 0; + } + } + var dots = R._parseDots(gradient); + if (!dots) { + return null; + } + if (element.gradient) { + SVG.defs.removeChild(element.gradient); + delete element.gradient; + } + + id = id.replace(/[\(\)\s,\xb0#]/g, "-"); + el = $(type + "Gradient", {id: id}); + element.gradient = el; + $(el, type == "radial" ? { + fx: fx, + fy: fy + } : { + x1: vector[0], + y1: vector[1], + x2: vector[2], + y2: vector[3], + gradientTransform: element.matrix.invert() + }); + SVG.defs.appendChild(el); + for (var i = 0, ii = dots.length; i < ii; i++) { + el.appendChild($("stop", { + offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%", + "stop-color": dots[i].color || "#fff" + })); + } + } + $(o, { + fill: "url(#" + id + ")", + opacity: 1, + "fill-opacity": 1 + }); + s.fill = E; + s.opacity = 1; + s.fillOpacity = 1; + return 1; + }, + updatePosition = function (o) { + var bbox = o.getBBox(1); + $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"}); + }, + addArrow = function (o, value, isEnd) { + if (o.type == "path") { + var values = Str(value).toLowerCase().split("-"), + p = o.paper, + se = isEnd ? "end" : "start", + node = o.node, + attrs = o.attrs, + stroke = attrs["stroke-width"], + i = values.length, + type = "classic", + from, + to, + dx, + refX, + attr, + w = 3, + h = 3, + t = 5; + while (i--) { + switch (values[i]) { + case "block": + case "classic": + case "oval": + case "diamond": + case "open": + case "none": + type = values[i]; + break; + case "wide": h = 5; break; + case "narrow": h = 2; break; + case "long": w = 5; break; + case "short": w = 2; break; + } + } + if (type == "open") { + w += 2; + h += 2; + t += 2; + dx = 1; + refX = isEnd ? 4 : 1; + attr = { + fill: "none", + stroke: attrs.stroke + }; + } else { + refX = dx = w / 2; + attr = { + fill: attrs.stroke, + stroke: "none" + }; + } + if (o._.arrows) { + if (isEnd) { + o._.arrows.endPath && markerCounter[o._.arrows.endPath]--; + o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--; + } else { + o._.arrows.startPath && markerCounter[o._.arrows.startPath]--; + o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--; + } + } else { + o._.arrows = {}; + } + if (type != "none") { + var pathId = "raphael-marker-" + type, + markerId = "raphael-marker-" + se + type + w + h; + if (!R._g.doc.getElementById(pathId)) { + p.defs.appendChild($($("path"), { + "stroke-linecap": "round", + d: markers[type], + id: pathId + })); + markerCounter[pathId] = 1; + } else { + markerCounter[pathId]++; + } + var marker = R._g.doc.getElementById(markerId), + use; + if (!marker) { + marker = $($("marker"), { + id: markerId, + markerHeight: h, + markerWidth: w, + orient: "auto", + refX: refX, + refY: h / 2 + }); + use = $($("use"), { + "xlink:href": "#" + pathId, + transform: (isEnd ? " rotate(180 " + w / 2 + " " + h / 2 + ") " : S) + "scale(" + w / t + "," + h / t + ")", + "stroke-width": 1 / ((w / t + h / t) / 2) + }); + marker.appendChild(use); + p.defs.appendChild(marker); + markerCounter[markerId] = 1; + } else { + markerCounter[markerId]++; + use = marker.getElementsByTagName("use")[0]; + } + $(use, attr); + var delta = dx * (type != "diamond" && type != "oval"); + if (isEnd) { + from = o._.arrows.startdx * stroke || 0; + to = R.getTotalLength(attrs.path) - delta * stroke; + } else { + from = delta * stroke; + to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0); + } + attr = {}; + attr["marker-" + se] = "url(#" + markerId + ")"; + if (to || from) { + attr.d = Raphael.getSubpath(attrs.path, from, to); + } + $(node, attr); + o._.arrows[se + "Path"] = pathId; + o._.arrows[se + "Marker"] = markerId; + o._.arrows[se + "dx"] = delta; + o._.arrows[se + "Type"] = type; + o._.arrows[se + "String"] = value; + } else { + if (isEnd) { + from = o._.arrows.startdx * stroke || 0; + to = R.getTotalLength(attrs.path) - from; + } else { + from = 0; + to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0); + } + o._.arrows[se + "Path"] && $(node, {d: Raphael.getSubpath(attrs.path, from, to)}); + delete o._.arrows[se + "Path"]; + delete o._.arrows[se + "Marker"]; + delete o._.arrows[se + "dx"]; + delete o._.arrows[se + "Type"]; + delete o._.arrows[se + "String"]; + } + for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) { + var item = R._g.doc.getElementById(attr); + item && item.parentNode.removeChild(item); + } + } + }, + dasharray = { + "": [0], + "none": [0], + "-": [3, 1], + ".": [1, 1], + "-.": [3, 1, 1, 1], + "-..": [3, 1, 1, 1, 1, 1], + ". ": [1, 3], + "- ": [4, 3], + "--": [8, 3], + "- .": [4, 3, 1, 3], + "--.": [8, 3, 1, 3], + "--..": [8, 3, 1, 3, 1, 3] + }, + addDashes = function (o, value, params) { + value = dasharray[Str(value).toLowerCase()]; + if (value) { + var width = o.attrs["stroke-width"] || "1", + butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0, + dashes = [], + i = value.length; + while (i--) { + dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt; + } + $(o.node, {"stroke-dasharray": dashes.join(",")}); + } + }, + setFillAndStroke = function (o, params) { + var node = o.node, + attrs = o.attrs, + vis = node.style.visibility; + node.style.visibility = "hidden"; + for (var att in params) { + if (params[has](att)) { + if (!R._availableAttrs[has](att)) { + continue; + } + var value = params[att]; + attrs[att] = value; + switch (att) { + case "blur": + o.blur(value); + break; + case "href": + case "title": + case "target": + var pn = node.parentNode; + if (pn.tagName.toLowerCase() != "a") { + var hl = $("a"); + pn.insertBefore(hl, node); + hl.appendChild(node); + pn = hl; + } + if (att == "target" && value == "blank") { + pn.setAttributeNS(xlink, "show", "new"); + } else { + pn.setAttributeNS(xlink, att, value); + } + break; + case "cursor": + node.style.cursor = value; + break; + case "transform": + o.transform(value); + break; + case "arrow-start": + addArrow(o, value); + break; + case "arrow-end": + addArrow(o, value, 1); + break; + case "clip-rect": + var rect = Str(value).split(separator); + if (rect.length == 4) { + o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode); + var el = $("clipPath"), + rc = $("rect"); + el.id = R.createUUID(); + $(rc, { + x: rect[0], + y: rect[1], + width: rect[2], + height: rect[3] + }); + el.appendChild(rc); + o.paper.defs.appendChild(el); + $(node, {"clip-path": "url(#" + el.id + ")"}); + o.clip = rc; + } + if (!value) { + var clip = R._g.doc.getElementById(node.getAttribute("clip-path").replace(/(^url\(#|\)$)/g, E)); + clip && clip.parentNode.removeChild(clip); + $(node, {"clip-path": E}); + delete o.clip; + } + break; + case "path": + if (o.type == "path") { + $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"}); + o._.dirty = 1; + if (o._.arrows) { + "startString" in o._.arrows && addArrow(o, o._.arrows.startString); + "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1); + } + } + break; + case "width": + node.setAttribute(att, value); + o._.dirty = 1; + if (attrs.fx) { + att = "x"; + value = attrs.x; + } else { + break; + } + case "x": + if (attrs.fx) { + value = -attrs.x - (attrs.width || 0); + } + case "rx": + if (att == "rx" && o.type == "rect") { + break; + } + case "cx": + node.setAttribute(att, value); + o.pattern && updatePosition(o); + o._.dirty = 1; + break; + case "height": + node.setAttribute(att, value); + o._.dirty = 1; + if (attrs.fy) { + att = "y"; + value = attrs.y; + } else { + break; + } + case "y": + if (attrs.fy) { + value = -attrs.y - (attrs.height || 0); + } + case "ry": + if (att == "ry" && o.type == "rect") { + break; + } + case "cy": + node.setAttribute(att, value); + o.pattern && updatePosition(o); + o._.dirty = 1; + break; + case "r": + if (o.type == "rect") { + $(node, {rx: value, ry: value}); + } else { + node.setAttribute(att, value); + } + o._.dirty = 1; + break; + case "src": + if (o.type == "image") { + node.setAttributeNS(xlink, "href", value); + } + break; + case "stroke-width": + if (o._.sx != 1 || o._.sy != 1) { + value /= mmax(abs(o._.sx), abs(o._.sy)) || 1; + } + if (o.paper._vbSize) { + value *= o.paper._vbSize; + } + node.setAttribute(att, value); + if (attrs["stroke-dasharray"]) { + addDashes(o, attrs["stroke-dasharray"], params); + } + if (o._.arrows) { + "startString" in o._.arrows && addArrow(o, o._.arrows.startString); + "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1); + } + break; + case "stroke-dasharray": + addDashes(o, value, params); + break; + case "fill": + var isURL = Str(value).match(R._ISURL); + if (isURL) { + el = $("pattern"); + var ig = $("image"); + el.id = R.createUUID(); + $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1}); + $(ig, {x: 0, y: 0, "xlink:href": isURL[1]}); + el.appendChild(ig); + + (function (el) { + R._preload(isURL[1], function () { + var w = this.offsetWidth, + h = this.offsetHeight; + $(el, {width: w, height: h}); + $(ig, {width: w, height: h}); + o.paper.safari(); + }); + })(el); + o.paper.defs.appendChild(el); + node.style.fill = "url(#" + el.id + ")"; + $(node, {fill: "url(#" + el.id + ")"}); + o.pattern = el; + o.pattern && updatePosition(o); + break; + } + var clr = R.getRGB(value); + if (!clr.error) { + delete params.gradient; + delete attrs.gradient; + !R.is(attrs.opacity, "undefined") && + R.is(params.opacity, "undefined") && + $(node, {opacity: attrs.opacity}); + !R.is(attrs["fill-opacity"], "undefined") && + R.is(params["fill-opacity"], "undefined") && + $(node, {"fill-opacity": attrs["fill-opacity"]}); + } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) { + if ("opacity" in attrs || "fill-opacity" in attrs) { + var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E)); + if (gradient) { + var stops = gradient.getElementsByTagName("stop"); + $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)}); + } + } + attrs.gradient = value; + attrs.fill = "none"; + break; + } + clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity}); + case "stroke": + clr = R.getRGB(value); + node.setAttribute(att, clr.hex); + att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity}); + if (att == "stroke" && o._.arrows) { + "startString" in o._.arrows && addArrow(o, o._.arrows.startString); + "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1); + } + break; + case "gradient": + (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value); + break; + case "opacity": + if (attrs.gradient && !attrs[has]("stroke-opacity")) { + $(node, {"stroke-opacity": value > 1 ? value / 100 : value}); + } + // fall + case "fill-opacity": + if (attrs.gradient) { + gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E)); + if (gradient) { + stops = gradient.getElementsByTagName("stop"); + $(stops[stops.length - 1], {"stop-opacity": value}); + } + break; + } + default: + att == "font-size" && (value = toInt(value, 10) + "px"); + var cssrule = att.replace(/(\-.)/g, function (w) { + return w.substring(1).toUpperCase(); + }); + node.style[cssrule] = value; + o._.dirty = 1; + node.setAttribute(att, value); + break; + } + } + } + + tuneText(o, params); + node.style.visibility = vis; + }, + leading = 1.2, + tuneText = function (el, params) { + if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) { + return; + } + var a = el.attrs, + node = el.node, + fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10; + + if (params[has]("text")) { + a.text = params.text; + while (node.firstChild) { + node.removeChild(node.firstChild); + } + var texts = Str(params.text).split("\n"), + tspans = [], + tspan; + for (var i = 0, ii = texts.length; i < ii; i++) { + tspan = $("tspan"); + i && $(tspan, {dy: fontSize * leading, x: a.x}); + tspan.appendChild(R._g.doc.createTextNode(texts[i])); + node.appendChild(tspan); + tspans[i] = tspan; + } + } else { + tspans = node.getElementsByTagName("tspan"); + for (i = 0, ii = tspans.length; i < ii; i++) if (i) { + $(tspans[i], {dy: fontSize * leading, x: a.x}); + } else { + $(tspans[0], {dy: 0}); + } + } + $(node, {x: a.x, y: a.y}); + el._.dirty = 1; + var bb = el._getBBox(), + dif = a.y - (bb.y + bb.height / 2); + dif && R.is(dif, "finite") && $(tspans[0], {dy: dif}); + }, + Element = function (node, svg) { + var X = 0, + Y = 0; + + this[0] = this.node = node; + + node.raphael = true; + + this.id = R._oid++; + node.raphaelid = this.id; + this.matrix = R.matrix(); + this.realPath = null; + + this.paper = svg; + this.attrs = this.attrs || {}; + this._ = { + transform: [], + sx: 1, + sy: 1, + deg: 0, + dx: 0, + dy: 0, + dirty: 1 + }; + !svg.bottom && (svg.bottom = this); + + this.prev = svg.top; + svg.top && (svg.top.next = this); + svg.top = this; + + this.next = null; + }, + elproto = R.el; + + Element.prototype = elproto; + elproto.constructor = Element; + + R._engine.path = function (pathString, SVG) { + var el = $("path"); + SVG.canvas && SVG.canvas.appendChild(el); + var p = new Element(el, SVG); + p.type = "path"; + setFillAndStroke(p, { + fill: "none", + stroke: "#000", + path: pathString + }); + return p; + }; + + elproto.rotate = function (deg, cx, cy) { + if (this.removed) { + return this; + } + deg = Str(deg).split(separator); + if (deg.length - 1) { + cx = toFloat(deg[1]); + cy = toFloat(deg[2]); + } + deg = toFloat(deg[0]); + (cy == null) && (cx = cy); + if (cx == null || cy == null) { + var bbox = this.getBBox(1); + cx = bbox.x + bbox.width / 2; + cy = bbox.y + bbox.height / 2; + } + this.transform(this._.transform.concat([["r", deg, cx, cy]])); + return this; + }; + + elproto.scale = function (sx, sy, cx, cy) { + if (this.removed) { + return this; + } + sx = Str(sx).split(separator); + if (sx.length - 1) { + sy = toFloat(sx[1]); + cx = toFloat(sx[2]); + cy = toFloat(sx[3]); + } + sx = toFloat(sx[0]); + (sy == null) && (sy = sx); + (cy == null) && (cx = cy); + if (cx == null || cy == null) { + var bbox = this.getBBox(1); + } + cx = cx == null ? bbox.x + bbox.width / 2 : cx; + cy = cy == null ? bbox.y + bbox.height / 2 : cy; + this.transform(this._.transform.concat([["s", sx, sy, cx, cy]])); + return this; + }; + + elproto.translate = function (dx, dy) { + if (this.removed) { + return this; + } + dx = Str(dx).split(separator); + if (dx.length - 1) { + dy = toFloat(dx[1]); + } + dx = toFloat(dx[0]) || 0; + dy = +dy || 0; + this.transform(this._.transform.concat([["t", dx, dy]])); + return this; + }; + + elproto.transform = function (tstr) { + var _ = this._; + if (tstr == null) { + return _.transform; + } + R._extractTransform(this, tstr); + + this.clip && $(this.clip, {transform: this.matrix.invert()}); + this.pattern && updatePosition(this); + this.node && $(this.node, {transform: this.matrix}); + + if (_.sx != 1 || _.sy != 1) { + var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1; + this.attr({"stroke-width": sw}); + } + + return this; + }; + + elproto.hide = function () { + !this.removed && this.paper.safari(this.node.style.display = "none"); + return this; + }; + + elproto.show = function () { + !this.removed && this.paper.safari(this.node.style.display = ""); + return this; + }; + + elproto.remove = function () { + if (this.removed) { + return; + } + eve.unbind("*.*." + this.id); + R._tear(this, this.paper); + this.node.parentNode.removeChild(this.node); + for (var i in this) { + delete this[i]; + } + this.removed = true; + }; + elproto._getBBox = function () { + if (this.node.style.display == "none") { + this.show(); + var hide = true; + } + var bbox = {}; + try { + bbox = this.node.getBBox(); + } catch(e) { + // Firefox 3.0.x plays badly here + } finally { + bbox = bbox || {}; + } + hide && this.hide(); + return bbox; + }; + + elproto.attr = function (name, value) { + if (this.removed) { + return this; + } + if (name == null) { + var res = {}; + for (var a in this.attrs) if (this.attrs[has](a)) { + res[a] = this.attrs[a]; + } + res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; + res.transform = this._.transform; + return res; + } + if (value == null && R.is(name, "string")) { + if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) { + return this.attrs.gradient; + } + if (name == "transform") { + return this._.transform; + } + var names = name.split(separator), + out = {}; + for (var i = 0, ii = names.length; i < ii; i++) { + name = names[i]; + if (name in this.attrs) { + out[name] = this.attrs[name]; + } else if (R.is(this.paper.customAttributes[name], "function")) { + out[name] = this.paper.customAttributes[name].def; + } else { + out[name] = R._availableAttrs[name]; + } + } + return ii - 1 ? out : out[names[0]]; + } + if (value == null && R.is(name, "array")) { + out = {}; + for (i = 0, ii = name.length; i < ii; i++) { + out[name[i]] = this.attr(name[i]); + } + return out; + } + if (value != null) { + var params = {}; + params[name] = value; + } else if (name != null && R.is(name, "object")) { + params = name; + } + for (var key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) { + var par = this.paper.customAttributes[key].apply(this, [].concat(params[key])); + this.attrs[key] = params[key]; + for (var subkey in par) if (par[has](subkey)) { + params[subkey] = par[subkey]; + } + } + setFillAndStroke(this, params); + return this; + }; + + elproto.toFront = function () { + if (this.removed) { + return this; + } + this.node.parentNode.appendChild(this.node); + var svg = this.paper; + svg.top != this && R._tofront(this, svg); + return this; + }; + + elproto.toBack = function () { + if (this.removed) { + return this; + } + if (this.node.parentNode.firstChild != this.node) { + this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild); + R._toback(this, this.paper); + var svg = this.paper; + } + return this; + }; + + elproto.insertAfter = function (element) { + if (this.removed) { + return this; + } + var node = element.node || element[element.length - 1].node; + if (node.nextSibling) { + node.parentNode.insertBefore(this.node, node.nextSibling); + } else { + node.parentNode.appendChild(this.node); + } + R._insertafter(this, element, this.paper); + return this; + }; + + elproto.insertBefore = function (element) { + if (this.removed) { + return this; + } + var node = element.node || element[0].node; + node.parentNode.insertBefore(this.node, node); + R._insertbefore(this, element, this.paper); + return this; + }; + elproto.blur = function (size) { + // Experimental. No Safari support. Use it on your own risk. + var t = this; + if (+size !== 0) { + var fltr = $("filter"), + blur = $("feGaussianBlur"); + t.attrs.blur = size; + fltr.id = R.createUUID(); + $(blur, {stdDeviation: +size || 1.5}); + fltr.appendChild(blur); + t.paper.defs.appendChild(fltr); + t._blur = fltr; + $(t.node, {filter: "url(#" + fltr.id + ")"}); + } else { + if (t._blur) { + t._blur.parentNode.removeChild(t._blur); + delete t._blur; + delete t.attrs.blur; + } + t.node.removeAttribute("filter"); + } + }; + R._engine.circle = function (svg, x, y, r) { + var el = $("circle"); + svg.canvas && svg.canvas.appendChild(el); + var res = new Element(el, svg); + res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"}; + res.type = "circle"; + $(el, res.attrs); + return res; + }; + R._engine.rect = function (svg, x, y, w, h, r) { + var el = $("rect"); + svg.canvas && svg.canvas.appendChild(el); + var res = new Element(el, svg); + res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"}; + res.type = "rect"; + $(el, res.attrs); + return res; + }; + R._engine.ellipse = function (svg, x, y, rx, ry) { + var el = $("ellipse"); + svg.canvas && svg.canvas.appendChild(el); + var res = new Element(el, svg); + res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"}; + res.type = "ellipse"; + $(el, res.attrs); + return res; + }; + R._engine.image = function (svg, src, x, y, w, h) { + var el = $("image"); + $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"}); + el.setAttributeNS(xlink, "href", src); + svg.canvas && svg.canvas.appendChild(el); + var res = new Element(el, svg); + res.attrs = {x: x, y: y, width: w, height: h, src: src}; + res.type = "image"; + return res; + }; + R._engine.text = function (svg, x, y, text) { + var el = $("text"); + // $(el, {x: x, y: y, "text-anchor": "middle"}); + svg.canvas && svg.canvas.appendChild(el); + var res = new Element(el, svg); + res.attrs = { + x: x, + y: y, + "text-anchor": "middle", + text: text, + font: R._availableAttrs.font, + stroke: "none", + fill: "#000" + }; + res.type = "text"; + setFillAndStroke(res, res.attrs); + return res; + }; + R._engine.setSize = function (width, height) { + this.width = width || this.width; + this.height = height || this.height; + this.canvas.setAttribute("width", this.width); + this.canvas.setAttribute("height", this.height); + if (this._viewBox) { + this.setViewBox.apply(this, this._viewBox); + } + return this; + }; + R._engine.create = function () { + var con = R._getContainer.apply(0, arguments), + container = con && con.container, + x = con.x, + y = con.y, + width = con.width, + height = con.height; + if (!container) { + throw new Error("SVG container not found."); + } + var cnvs = $("svg"), + css = "overflow:hidden;", + isFloating; + x = x || 0; + y = y || 0; + width = width || 512; + height = height || 342; + $(cnvs, { + height: height, + version: 1.1, + width: width, + xmlns: "http://www.w3.org/2000/svg" + }); + if (container == 1) { + cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px"; + R._g.doc.body.appendChild(cnvs); + isFloating = 1; + } else { + cnvs.style.cssText = css + "position:relative"; + if (container.firstChild) { + container.insertBefore(cnvs, container.firstChild); + } else { + container.appendChild(cnvs); + } + } + container = new R._Paper; + container.width = width; + container.height = height; + container.canvas = cnvs; + // plugins.call(container, container, R.fn); + container.clear(); + container._left = container._top = 0; + isFloating && (container.renderfix = function () {}); + container.renderfix(); + return container; + }; + R._engine.setViewBox = function (x, y, w, h, fit) { + eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]); + var size = mmax(w / this.width, h / this.height), + top = this.top, + aspectRatio = fit ? "meet" : "xMinYMin", + vb, + sw; + if (x == null) { + if (this._vbSize) { + size = 1; + } + delete this._vbSize; + vb = "0 0 " + this.width + S + this.height; + } else { + this._vbSize = size; + vb = x + S + y + S + w + S + h; + } + $(this.canvas, { + viewBox: vb, + preserveAspectRatio: aspectRatio + }); + while (size && top) { + sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1; + top.attr({"stroke-width": sw}); + top._.dirty = 1; + top._.dirtyT = 1; + top = top.prev; + } + this._viewBox = [x, y, w, h, !!fit]; + return this; + }; + + R.prototype.renderfix = function () { + var cnvs = this.canvas, + s = cnvs.style, + pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix(), + left = -pos.e % 1, + top = -pos.f % 1; + if (left || top) { + if (left) { + this._left = (this._left + left) % 1; + s.left = this._left + "px"; + } + if (top) { + this._top = (this._top + top) % 1; + s.top = this._top + "px"; + } + } + }; + + R.prototype.clear = function () { + R.eve("clear", this); + var c = this.canvas; + while (c.firstChild) { + c.removeChild(c.firstChild); + } + this.bottom = this.top = null; + (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version)); + c.appendChild(this.desc); + c.appendChild(this.defs = $("defs")); + }; + + R.prototype.remove = function () { + eve("remove", this); + this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); + for (var i in this) { + this[i] = removed(i); + } + }; + var setproto = R.st; + for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) { + setproto[method] = (function (methodname) { + return function () { + var arg = arguments; + return this.forEach(function (el) { + el[methodname].apply(el, arg); + }); + }; + })(method); + } +}(window.Raphael); + +// ┌─────────────────────────────────────────────────────────────────────┐ \\ +// │ Raphaël 2 - JavaScript Vector Library │ \\ +// ├─────────────────────────────────────────────────────────────────────┤ \\ +// │ VML Module │ \\ +// ├─────────────────────────────────────────────────────────────────────┤ \\ +// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com) │ \\ +// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com) │ \\ +// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\ +// └─────────────────────────────────────────────────────────────────────┘ \\ +window.Raphael.vml && function (R) { + var has = "hasOwnProperty", + Str = String, + toFloat = parseFloat, + math = Math, + round = math.round, + mmax = math.max, + mmin = math.min, + abs = math.abs, + fillString = "fill", + separator = /[, ]+/, + eve = R.eve, + ms = " progid:DXImageTransform.Microsoft", + S = " ", + E = "", + map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"}, + bites = /([clmz]),?([^clmz]*)/gi, + blurregexp = / progid:\S+Blur\([^\)]+\)/g, + val = /-?[^,\s-]+/g, + cssDot = "position:absolute;left:0;top:0;width:1px;height:1px", + zoom = 21600, + pathTypes = {path: 1, rect: 1, image: 1}, + ovalTypes = {circle: 1, ellipse: 1}, + path2vml = function (path) { + var total = /[ahqstv]/ig, + command = R._pathToAbsolute; + Str(path).match(total) && (command = R._path2curve); + total = /[clmz]/g; + if (command == R._pathToAbsolute && !Str(path).match(total)) { + var res = Str(path).replace(bites, function (all, command, args) { + var vals = [], + isMove = command.toLowerCase() == "m", + res = map[command]; + args.replace(val, function (value) { + if (isMove && vals.length == 2) { + res += vals + map[command == "m" ? "l" : "L"]; + vals = []; + } + vals.push(round(value * zoom)); + }); + return res + vals; + }); + return res; + } + var pa = command(path), p, r; + res = []; + for (var i = 0, ii = pa.length; i < ii; i++) { + p = pa[i]; + r = pa[i][0].toLowerCase(); + r == "z" && (r = "x"); + for (var j = 1, jj = p.length; j < jj; j++) { + r += round(p[j] * zoom) + (j != jj - 1 ? "," : E); + } + res.push(r); + } + return res.join(S); + }, + compensation = function (deg, dx, dy) { + var m = R.matrix(); + m.rotate(-deg, .5, .5); + return { + dx: m.x(dx, dy), + dy: m.y(dx, dy) + }; + }, + setCoords = function (p, sx, sy, dx, dy, deg) { + var _ = p._, + m = p.matrix, + fillpos = _.fillpos, + o = p.node, + s = o.style, + y = 1, + flip = "", + dxdy, + kx = zoom / sx, + ky = zoom / sy; + s.visibility = "hidden"; + if (!sx || !sy) { + return; + } + o.coordsize = abs(kx) + S + abs(ky); + s.rotation = deg * (sx * sy < 0 ? -1 : 1); + if (deg) { + var c = compensation(deg, dx, dy); + dx = c.dx; + dy = c.dy; + } + sx < 0 && (flip += "x"); + sy < 0 && (flip += " y") && (y = -1); + s.flip = flip; + o.coordorigin = (dx * -kx) + S + (dy * -ky); + if (fillpos || _.fillsize) { + var fill = o.getElementsByTagName(fillString); + fill = fill && fill[0]; + o.removeChild(fill); + if (fillpos) { + c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1])); + fill.position = c.dx * y + S + c.dy * y; + } + if (_.fillsize) { + fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy); + } + o.appendChild(fill); + } + s.visibility = "visible"; + }; + R.toString = function () { + return "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version; + }; + addArrow = function (o, value, isEnd) { + var values = Str(value).toLowerCase().split("-"), + se = isEnd ? "end" : "start", + i = values.length, + type = "classic", + w = "medium", + h = "medium"; + while (i--) { + switch (values[i]) { + case "block": + case "classic": + case "oval": + case "diamond": + case "open": + case "none": + type = values[i]; + break; + case "wide": + case "narrow": h = values[i]; break; + case "long": + case "short": w = values[i]; break; + } + } + var stroke = o.node.getElementsByTagName("stroke")[0]; + stroke[se + "arrow"] = type; + stroke[se + "arrowlength"] = w; + stroke[se + "arrowwidth"] = h; + }; + setFillAndStroke = function (o, params) { + // o.paper.canvas.style.display = "none"; + o.attrs = o.attrs || {}; + var node = o.node, + a = o.attrs, + s = node.style, + xy, + newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r), + isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry), + res = o; + + + for (var par in params) if (params[has](par)) { + a[par] = params[par]; + } + if (newpath) { + a.path = R._getPath[o.type](o); + o._.dirty = 1; + } + params.href && (node.href = params.href); + params.title && (node.title = params.title); + params.target && (node.target = params.target); + params.cursor && (s.cursor = params.cursor); + "blur" in params && o.blur(params.blur); + if (params.path && o.type == "path" || newpath) { + node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path); + if (o.type == "image") { + o._.fillpos = [a.x, a.y]; + o._.fillsize = [a.width, a.height]; + setCoords(o, 1, 1, 0, 0, 0); + } + } + "transform" in params && o.transform(params.transform); + if (isOval) { + var cx = +a.cx, + cy = +a.cy, + rx = +a.rx || +a.r || 0, + ry = +a.ry || +a.r || 0; + node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom)); + } + if ("clip-rect" in params) { + var rect = Str(params["clip-rect"]).split(separator); + if (rect.length == 4) { + rect[2] = +rect[2] + (+rect[0]); + rect[3] = +rect[3] + (+rect[1]); + var div = node.clipRect || R._g.doc.createElement("div"), + dstyle = div.style; + dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect); + if (!node.clipRect) { + dstyle.position = "absolute"; + dstyle.top = 0; + dstyle.left = 0; + dstyle.width = o.paper.width + "px"; + dstyle.height = o.paper.height + "px"; + node.parentNode.insertBefore(div, node); + div.appendChild(node); + node.clipRect = div; + } + } + if (!params["clip-rect"]) { + node.clipRect && (node.clipRect.style.clip = E); + } + } + if (o.textpath) { + var textpathStyle = o.textpath.style; + params.font && (textpathStyle.font = params.font); + params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"'); + params["font-size"] && (textpathStyle.fontSize = params["font-size"]); + params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]); + params["font-style"] && (textpathStyle.fontStyle = params["font-style"]); + } + if ("arrow-start" in params) { + addArrow(res, params["arrow-start"]); + } + if ("arrow-end" in params) { + addArrow(res, params["arrow-end"], 1); + } + if (params.opacity != null || + params["stroke-width"] != null || + params.fill != null || + params.src != null || + params.stroke != null || + params["stroke-width"] != null || + params["stroke-opacity"] != null || + params["fill-opacity"] != null || + params["stroke-dasharray"] != null || + params["stroke-miterlimit"] != null || + params["stroke-linejoin"] != null || + params["stroke-linecap"] != null) { + var fill = node.getElementsByTagName(fillString), + newfill = false; + fill = fill && fill[0]; + !fill && (newfill = fill = createNode(fillString)); + if (o.type == "image" && params.src) { + fill.src = params.src; + } + params.fill && (fill.on = true); + if (fill.on == null || params.fill == "none" || params.fill === null) { + fill.on = false; + } + if (fill.on && params.fill) { + var isURL = Str(params.fill).match(R._ISURL); + if (isURL) { + fill.parentNode == node && node.removeChild(fill); + fill.rotate = true; + fill.src = isURL[1]; + fill.type = "tile"; + var bbox = o.getBBox(1); + fill.position = bbox.x + S + bbox.y; + o._.fillpos = [bbox.x, bbox.y]; + + R._preload(isURL[1], function () { + o._.fillsize = [this.offsetWidth, this.offsetHeight]; + }); + } else { + fill.color = R.getRGB(params.fill).hex; + fill.src = E; + fill.type = "solid"; + if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) { + a.fill = "none"; + a.gradient = params.fill; + fill.rotate = false; + } + } + } + if ("fill-opacity" in params || "opacity" in params) { + var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1); + opacity = mmin(mmax(opacity, 0), 1); + fill.opacity = opacity; + if (fill.src) { + fill.color = "none"; + } + } + node.appendChild(fill); + var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]), + newstroke = false; + !stroke && (newstroke = stroke = createNode("stroke")); + if ((params.stroke && params.stroke != "none") || + params["stroke-width"] || + params["stroke-opacity"] != null || + params["stroke-dasharray"] || + params["stroke-miterlimit"] || + params["stroke-linejoin"] || + params["stroke-linecap"]) { + stroke.on = true; + } + (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false); + var strokeColor = R.getRGB(params.stroke); + stroke.on && params.stroke && (stroke.color = strokeColor.hex); + opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1); + var width = (toFloat(params["stroke-width"]) || 1) * .75; + opacity = mmin(mmax(opacity, 0), 1); + params["stroke-width"] == null && (width = a["stroke-width"]); + params["stroke-width"] && (stroke.weight = width); + width && width < 1 && (opacity *= width) && (stroke.weight = 1); + stroke.opacity = opacity; + + params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter"); + stroke.miterlimit = params["stroke-miterlimit"] || 8; + params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round"); + if (params["stroke-dasharray"]) { + var dasharray = { + "-": "shortdash", + ".": "shortdot", + "-.": "shortdashdot", + "-..": "shortdashdotdot", + ". ": "dot", + "- ": "dash", + "--": "longdash", + "- .": "dashdot", + "--.": "longdashdot", + "--..": "longdashdotdot" + }; + stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E; + } + newstroke && node.appendChild(stroke); + } + if (res.type == "text") { + res.paper.canvas.style.display = E; + var span = res.paper.span, + m = 100, + fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/); + s = span.style; + a.font && (s.font = a.font); + a["font-family"] && (s.fontFamily = a["font-family"]); + a["font-weight"] && (s.fontWeight = a["font-weight"]); + a["font-style"] && (s.fontStyle = a["font-style"]); + fontSize = toFloat(fontSize ? fontSize[0] : a["font-size"]); + s.fontSize = fontSize * m + "px"; + res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/")); + var brect = span.getBoundingClientRect(); + res.W = a.w = (brect.right - brect.left) / m; + res.H = a.h = (brect.bottom - brect.top) / m; + // res.paper.canvas.style.display = "none"; + res.X = a.x; + res.Y = a.y + res.H / 2; + + ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1)); + var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"]; + for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) { + res._.dirty = 1; + break; + } + + // text-anchor emulation + switch (a["text-anchor"]) { + case "start": + res.textpath.style["v-text-align"] = "left"; + res.bbx = res.W / 2; + break; + case "end": + res.textpath.style["v-text-align"] = "right"; + res.bbx = -res.W / 2; + break; + default: + res.textpath.style["v-text-align"] = "center"; + res.bbx = 0; + break; + } + res.textpath.style["v-text-kern"] = true; + } + // res.paper.canvas.style.display = E; + }; + addGradientFill = function (o, gradient, fill) { + o.attrs = o.attrs || {}; + var attrs = o.attrs, + opacity, + oindex, + type = "linear", + fxfy = ".5 .5"; + o.attrs.gradient = gradient; + gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) { + type = "radial"; + if (fx && fy) { + fx = toFloat(fx); + fy = toFloat(fy); + pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5); + fxfy = fx + S + fy; + } + return E; + }); + gradient = gradient.split(/\s*\-\s*/); + if (type == "linear") { + var angle = gradient.shift(); + angle = -toFloat(angle); + if (isNaN(angle)) { + return null; + } + } + var dots = R._parseDots(gradient); + if (!dots) { + return null; + } + o = o.shape || o.node; + if (dots.length) { + o.removeChild(fill); + fill.on = true; + fill.method = "none"; + fill.color = dots[0].color; + fill.color2 = dots[dots.length - 1].color; + var clrs = []; + for (var i = 0, ii = dots.length; i < ii; i++) { + dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color); + } + fill.colors = clrs.length ? clrs.join() : "0% " + fill.color; + if (type == "radial") { + fill.type = "gradientTitle"; + fill.focus = "100%"; + fill.focussize = "0 0"; + fill.focusposition = fxfy; + fill.angle = 0; + } else { + // fill.rotate= true; + fill.type = "gradient"; + fill.angle = (270 - angle) % 360; + } + o.appendChild(fill); + } + return 1; + }; + Element = function (node, vml) { + this[0] = this.node = node; + node.raphael = true; + this.id = R._oid++; + node.raphaelid = this.id; + this.X = 0; + this.Y = 0; + this.attrs = {}; + this.paper = vml; + this.matrix = R.matrix(); + this._ = { + transform: [], + sx: 1, + sy: 1, + dx: 0, + dy: 0, + deg: 0, + dirty: 1, + dirtyT: 1 + }; + !vml.bottom && (vml.bottom = this); + this.prev = vml.top; + vml.top && (vml.top.next = this); + vml.top = this; + this.next = null; + }; + var elproto = R.el; + + Element.prototype = elproto; + elproto.constructor = Element; + elproto.transform = function (tstr) { + if (tstr == null) { + return this._.transform; + } + var vbs = this.paper._viewBoxShift, + vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E, + oldt; + if (vbs) { + oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E); + } + R._extractTransform(this, vbt + tstr); + var matrix = this.matrix.clone(), + skew = this.skew, + o = this.node, + split, + isGrad = ~Str(this.attrs.fill).indexOf("-"), + isPatt = !Str(this.attrs.fill).indexOf("url("); + matrix.translate(-.5, -.5); + if (isPatt || isGrad || this.type == "image") { + skew.matrix = "1 0 0 1"; + skew.offset = "0 0"; + split = matrix.split(); + if ((isGrad && split.noRotation) || !split.isSimple) { + o.style.filter = matrix.toFilter(); + var bb = this.getBBox(), + bbt = this.getBBox(1), + dx = bb.x - bbt.x, + dy = bb.y - bbt.y; + o.coordorigin = (dx * -zoom) + S + (dy * -zoom); + setCoords(this, 1, 1, dx, dy, 0); + } else { + o.style.filter = E; + setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate); + } + } else { + o.style.filter = E; + skew.matrix = Str(matrix); + skew.offset = matrix.offset(); + } + oldt && (this._.transform = oldt); + return this; + }; + elproto.rotate = function (deg, cx, cy) { + if (this.removed) { + return this; + } + if (deg == null) { + return; + } + deg = Str(deg).split(separator); + if (deg.length - 1) { + cx = toFloat(deg[1]); + cy = toFloat(deg[2]); + } + deg = toFloat(deg[0]); + (cy == null) && (cx = cy); + if (cx == null || cy == null) { + var bbox = this.getBBox(1); + cx = bbox.x + bbox.width / 2; + cy = bbox.y + bbox.height / 2; + } + this._.dirtyT = 1; + this.transform(this._.transform.concat([["r", deg, cx, cy]])); + return this; + }; + elproto.translate = function (dx, dy) { + if (this.removed) { + return this; + } + dx = Str(dx).split(separator); + if (dx.length - 1) { + dy = toFloat(dx[1]); + } + dx = toFloat(dx[0]) || 0; + dy = +dy || 0; + if (this._.bbox) { + this._.bbox.x += dx; + this._.bbox.y += dy; + } + this.transform(this._.transform.concat([["t", dx, dy]])); + return this; + }; + elproto.scale = function (sx, sy, cx, cy) { + if (this.removed) { + return this; + } + sx = Str(sx).split(separator); + if (sx.length - 1) { + sy = toFloat(sx[1]); + cx = toFloat(sx[2]); + cy = toFloat(sx[3]); + isNaN(cx) && (cx = null); + isNaN(cy) && (cy = null); + } + sx = toFloat(sx[0]); + (sy == null) && (sy = sx); + (cy == null) && (cx = cy); + if (cx == null || cy == null) { + var bbox = this.getBBox(1); + } + cx = cx == null ? bbox.x + bbox.width / 2 : cx; + cy = cy == null ? bbox.y + bbox.height / 2 : cy; + + this.transform(this._.transform.concat([["s", sx, sy, cx, cy]])); + this._.dirtyT = 1; + return this; + }; + elproto.hide = function () { + !this.removed && (this.node.style.display = "none"); + return this; + }; + elproto.show = function () { + !this.removed && (this.node.style.display = E); + return this; + }; + elproto._getBBox = function () { + if (this.removed) { + return {}; + } + if (this.type == "text") { + return { + x: this.X + (this.bbx || 0) - this.W / 2, + y: this.Y - this.H, + width: this.W, + height: this.H + }; + } else { + return pathDimensions(this.attrs.path); + } + }; + elproto.remove = function () { + if (this.removed) { + return; + } + R.eve.unbind("*.*." + this.id); + R._tear(this, this.paper); + this.node.parentNode.removeChild(this.node); + this.shape && this.shape.parentNode.removeChild(this.shape); + for (var i in this) { + delete this[i]; + } + this.removed = true; + }; + elproto.attr = function (name, value) { + if (this.removed) { + return this; + } + if (name == null) { + var res = {}; + for (var a in this.attrs) if (this.attrs[has](a)) { + res[a] = this.attrs[a]; + } + res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; + res.transform = this._.transform; + return res; + } + if (value == null && R.is(name, "string")) { + if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) { + return this.attrs.gradient; + } + var names = name.split(separator), + out = {}; + for (var i = 0, ii = names.length; i < ii; i++) { + name = names[i]; + if (name in this.attrs) { + out[name] = this.attrs[name]; + } else if (R.is(this.paper.customAttributes[name], "function")) { + out[name] = this.paper.customAttributes[name].def; + } else { + out[name] = R._availableAttrs[name]; + } + } + return ii - 1 ? out : out[names[0]]; + } + if (this.attrs && value == null && R.is(name, "array")) { + out = {}; + for (i = 0, ii = name.length; i < ii; i++) { + out[name[i]] = this.attr(name[i]); + } + return out; + } + var params; + if (value != null) { + params = {}; + params[name] = value; + } + value == null && R.is(name, "object") && (params = name); + for (var key in params) { + R.eve("attr." + key + "." + this.id, this, params[key]); + } + if (params) { + for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) { + var par = this.paper.customAttributes[key].apply(this, [][concat](params[key])); + this.attrs[key] = params[key]; + for (var subkey in par) if (par[has](subkey)) { + params[subkey] = par[subkey]; + } + } + // this.paper.canvas.style.display = "none"; + if (params.text && this.type == "text") { + this.textpath.string = params.text; + } + setFillAndStroke(this, params); + // this.paper.canvas.style.display = E; + } + return this; + }; + elproto.toFront = function () { + !this.removed && this.node.parentNode.appendChild(this.node); + this.paper && this.paper.top != this && R._tofront(this, this.paper); + return this; + }; + elproto.toBack = function () { + if (this.removed) { + return this; + } + if (this.node.parentNode.firstChild != this.node) { + this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild); + R._toback(this, this.paper); + } + return this; + }; + elproto.insertAfter = function (element) { + if (this.removed) { + return this; + } + if (element.constructor == R.st.constructor) { + element = element[element.length - 1]; + } + if (element.node.nextSibling) { + element.node.parentNode.insertBefore(this.node, element.node.nextSibling); + } else { + element.node.parentNode.appendChild(this.node); + } + R._insertafter(this, element, this.paper); + return this; + }; + elproto.insertBefore = function (element) { + if (this.removed) { + return this; + } + if (element.constructor == R.st.constructor) { + element = element[0]; + } + element.node.parentNode.insertBefore(this.node, element.node); + R._insertbefore(this, element, this.paper); + return this; + }; + elproto.blur = function (size) { + var s = this.node.runtimeStyle, + f = s.filter; + f = f.replace(blurregexp, E); + if (+size !== 0) { + this.attrs.blur = size; + s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")"; + s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5)); + } else { + s.filter = f; + s.margin = 0; + delete this.attrs.blur; + } + }; + + R._engine.path = function (pathString, vml) { + var el = createNode("shape"); + el.style.cssText = cssDot; + el.coordsize = zoom + S + zoom; + el.coordorigin = vml.coordorigin; + var p = new Element(el, vml), + attr = {fill: "none", stroke: "#000"}; + pathString && (attr.path = pathString); + p.type = "path"; + p.path = []; + p.Path = E; + setFillAndStroke(p, attr); + vml.canvas.appendChild(el); + var skew = createNode("skew"); + skew.on = true; + el.appendChild(skew); + p.skew = skew; + p.transform(E); + return p; + }; + R._engine.rect = function (vml, x, y, w, h, r) { + var path = R._rectPath(x, y, w, h, r), + res = vml.path(path), + a = res.attrs; + res.X = a.x = x; + res.Y = a.y = y; + res.W = a.width = w; + res.H = a.height = h; + a.r = r; + a.path = path; + res.type = "rect"; + return res; + }; + R._engine.ellipse = function (vml, x, y, rx, ry) { + var res = vml.path(), + a = res.attrs; + res.X = x - rx; + res.Y = y - ry; + res.W = rx * 2; + res.H = ry * 2; + res.type = "ellipse"; + setFillAndStroke(res, { + cx: x, + cy: y, + rx: rx, + ry: ry + }); + return res; + }; + R._engine.circle = function (vml, x, y, r) { + var res = vml.path(), + a = res.attrs; + res.X = x - r; + res.Y = y - r; + res.W = res.H = r * 2; + res.type = "circle"; + setFillAndStroke(res, { + cx: x, + cy: y, + r: r + }); + return res; + }; + R._engine.image = function (vml, src, x, y, w, h) { + var path = R._rectPath(x, y, w, h), + res = vml.path(path).attr({stroke: "none"}), + a = res.attrs, + node = res.node, + fill = node.getElementsByTagName(fillString)[0]; + a.src = src; + res.X = a.x = x; + res.Y = a.y = y; + res.W = a.width = w; + res.H = a.height = h; + a.path = path; + res.type = "image"; + fill.parentNode == node && node.removeChild(fill); + fill.rotate = true; + fill.src = src; + fill.type = "tile"; + res._.fillpos = [x, y]; + res._.fillsize = [w, h]; + node.appendChild(fill); + setCoords(res, 1, 1, 0, 0, 0); + return res; + }; + R._engine.text = function (vml, x, y, text) { + var el = createNode("shape"), + path = createNode("path"), + o = createNode("textpath"); + x = x || 0; + y = y || 0; + text = text || ""; + path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1); + path.textpathok = true; + o.string = Str(text); + o.on = true; + el.style.cssText = "position:absolute;left:0;top:0;width:1px;height:1px"; + el.coordsize = zoom + S + zoom; + el.coordorigin = "0 0"; + var p = new Element(el, vml), + attr = { + fill: "#000", + stroke: "none", + font: R._availableAttrs.font, + text: text + }; + p.shape = el; + p.path = path; + p.textpath = o; + p.type = "text"; + p.attrs.text = Str(text); + p.attrs.x = x; + p.attrs.y = y; + p.attrs.w = 1; + p.attrs.h = 1; + setFillAndStroke(p, attr); + el.appendChild(o); + el.appendChild(path); + vml.canvas.appendChild(el); + var skew = createNode("skew"); + skew.on = true; + el.appendChild(skew); + p.skew = skew; + p.transform(E); + return p; + }; + R._engine.setSize = function (width, height) { + var cs = this.canvas.style; + this.width = width; + this.height = height; + width == +width && (width += "px"); + height == +height && (height += "px"); + cs.width = width; + cs.height = height; + cs.clip = "rect(0 " + width + " " + height + " 0)"; + if (this._viewBox) { + setViewBox.apply(this, this._viewBox); + } + return this; + }; + R._engine.setViewBox = function (x, y, w, h, fit) { + R.eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]); + var width = this.width, + height = this.height, + size = 1 / mmax(w / width, h / height), + H, W; + if (fit) { + H = height / h; + W = width / w; + if (w * H < width) { + x -= (width - w * H) / 2 / H; + } + if (h * W < height) { + y -= (height - h * W) / 2 / W; + } + } + this._viewBox = [x, y, w, h, !!fit]; + this._viewBoxShift = { + dx: -x, + dy: -y, + scale: size + }; + this.forEach(function (el) { + el.transform("..."); + }); + return this; + }; + var createNode, + initWin = function (win) { + var doc = win.document; + doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); + try { + !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); + createNode = function (tagName) { + return doc.createElement(''); + }; + } catch (e) { + createNode = function (tagName) { + return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); + }; + } + }; + initWin(R._g.win); + R._engine.create = function () { + var con = R._getContainer.apply(0, arguments), + container = con.container, + height = con.height, + s, + width = con.width, + x = con.x, + y = con.y; + if (!container) { + throw new Error("VML container not found."); + } + var res = new R._Paper, + c = res.canvas = R._g.doc.createElement("div"), + cs = c.style; + x = x || 0; + y = y || 0; + width = width || 512; + height = height || 342; + res.width = width; + res.height = height; + width == +width && (width += "px"); + height == +height && (height += "px"); + res.coordsize = zoom * 1e3 + S + zoom * 1e3; + res.coordorigin = "0 0"; + res.span = R._g.doc.createElement("span"); + res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;"; + c.appendChild(res.span); + cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height); + if (container == 1) { + R._g.doc.body.appendChild(c); + cs.left = x + "px"; + cs.top = y + "px"; + cs.position = "absolute"; + } else { + if (container.firstChild) { + container.insertBefore(c, container.firstChild); + } else { + container.appendChild(c); + } + } + // plugins.call(res, res, R.fn); + res.renderfix = function () {}; + return res; + }; + R.prototype.clear = function () { + R.eve("clear", this); + this.canvas.innerHTML = E; + this.span = R._g.doc.createElement("span"); + this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;"; + this.canvas.appendChild(this.span); + this.bottom = this.top = null; + }; + R.prototype.remove = function () { + R.eve("remove", this); + this.canvas.parentNode.removeChild(this.canvas); + for (var i in this) { + this[i] = removed(i); + } + return true; + }; +}(window.Raphael); \ No newline at end of file diff --git a/assets/svg/Blank_USA,_w_territories.svg b/assets/svg/Blank_USA,_w_territories.svg new file mode 100755 index 00000000..f7815725 --- /dev/null +++ b/assets/svg/Blank_USA,_w_territories.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DC + + + + + MD + + + + + DE + + + + + NJ + + + + + CT + + + + + RI + + + + + MH + + + + + NH + + + + + VT + + + + + AS + + + + + MP + + + + + GU + + + + + VI + + + + + PR + + + + diff --git a/assets/svg/Blank_US_Map.svg b/assets/svg/Blank_US_Map.svg new file mode 100644 index 00000000..46ccbd39 --- /dev/null +++ b/assets/svg/Blank_US_Map.svg @@ -0,0 +1,326 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From df7fa373bcd8ee5cd8b6b39522fc199ed842f1d8 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 29 Apr 2021 22:46:07 +0100 Subject: [PATCH 06/34] US State Map colours changed --- application/views/awards/was/map.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/application/views/awards/was/map.php b/application/views/awards/was/map.php index 4b486b23..c725f4b0 100644 --- a/application/views/awards/was/map.php +++ b/application/views/awards/was/map.php @@ -37,7 +37,7 @@ foreach ($value as $key) { if($key != "") { if (strpos($key, '>W<') !== false) { - echo "{fill: '#e34949'},"; + echo "{fill: 'orange'},"; break; } if (strpos($key, '>C<') !== false) { @@ -45,7 +45,7 @@ break; } if (strpos($key, '-') !== false) { - echo "{fill: 'white'},"; + echo "{fill: '#e34949'},"; break; } } @@ -78,5 +78,11 @@
+ +
    +
  • Red - Not Worked
  • +
  • Orange - Worked but not confirmed
  • +
  • Green - Confirmed
  • +
From 23ecffafd085ce45160ef66754ad99f83633c993 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 29 Apr 2021 22:52:31 +0100 Subject: [PATCH 07/34] [WAS Map] Hide the smaller state legend. --- application/views/awards/was/map.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/views/awards/was/map.php b/application/views/awards/was/map.php index c725f4b0..a47d3830 100644 --- a/application/views/awards/was/map.php +++ b/application/views/awards/was/map.php @@ -22,13 +22,14 @@ + + + uri->segment(1) == "adif") { ?> diff --git a/assets/css/general.css b/assets/css/general.css index 6edef258..bea7aae1 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -232,4 +232,14 @@ color: #ffffff; .debug_main .card { margin-bottom: 10px; - } \ No newline at end of file + } + + .was-map-dialog .modal-dialog { + width: 100%; + } + +@media (min-width: 576px) { + .was-map-dialog .modal-dialog { + max-width: 70% !important; + } + } \ No newline at end of file From b95ef6d07de09aea25bed167dbc50fe47d9f224b Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Fri, 30 Apr 2021 15:27:18 +0100 Subject: [PATCH 09/34] [WAS][Map] Map now available for all bands & sats --- application/controllers/Awards.php | 6 +++--- application/views/awards/was/index.php | 8 ++------ application/views/interface_assets/footer.php | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/application/controllers/Awards.php b/application/controllers/Awards.php index e8df3659..33b54109 100644 --- a/application/controllers/Awards.php +++ b/application/controllers/Awards.php @@ -493,13 +493,13 @@ class Awards extends CI_Controller { $this->load->view('adif/data/exportall', $data); } - public function was_map() { + public function was_map($band_type) { $this->load->model('was'); $data['worked_bands'] = $this->was->get_worked_bands(); - $bands[] = 'SAT'; + $bands[] = $band_type; $data['bands'] = $bands; // Used for displaying selected band(s) in the table in the view @@ -509,7 +509,7 @@ class Awards extends CI_Controller { $postdata['worked'] = 1; $postdata['confirmed'] = 1; $postdata['notworked'] = 1; - $postdata['band'] = 'SAT'; + $postdata['band'] = $band_type; $data['was_array'] = $this->was->get_was_array($bands, $postdata); diff --git a/application/views/awards/was/index.php b/application/views/awards/was/index.php index 4aa86cd0..2d609fa6 100644 --- a/application/views/awards/was/index.php +++ b/application/views/awards/was/index.php @@ -66,12 +66,8 @@ - input->post('band') == "SAT") { ?> - - Show WAS Map

-
- - + input->post('band')) { ?> + input->post('band'); ?>)', cssClass: 'was-map-dialog', - message: $('
').load(site_url + '/awards/was_map') + message: $('
').load(site_url + '/awards/was_map/input->post('band'); ?>') }); } From de5671ef573a072aaa1445c42cfe2d3d6bce303e Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Fri, 30 Apr 2021 16:25:38 +0100 Subject: [PATCH 10/34] added some code comments --- application/controllers/Awards.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/application/controllers/Awards.php b/application/controllers/Awards.php index 33b54109..445b160e 100644 --- a/application/controllers/Awards.php +++ b/application/controllers/Awards.php @@ -493,29 +493,33 @@ class Awards extends CI_Controller { $this->load->view('adif/data/exportall', $data); } + /* + function was_map + + This displays the WAS map and requires the $band_type + */ public function was_map($band_type) { $this->load->model('was'); $data['worked_bands'] = $this->was->get_worked_bands(); - $bands[] = $band_type; - $data['bands'] = $bands; // Used for displaying selected band(s) in the table in the view - $postdata['lotw'] = 1; - $postdata['qsl'] = 1; - $postdata['worked'] = 1; - $postdata['confirmed'] = 1; - $postdata['notworked'] = 1; - $postdata['band'] = $band_type; + $postdata['lotw'] = 1; + $postdata['qsl'] = 1; + $postdata['worked'] = 1; + $postdata['confirmed'] = 1; + $postdata['notworked'] = 1; + $postdata['band'] = $band_type; $data['was_array'] = $this->was->get_was_array($bands, $postdata); $data['was_summary'] = $this->was->get_was_summary($bands); $data['page_title'] = ""; + $this->load->view('awards/was/map', $data); } } From a4132ee7f3e839414f60c8c0ca8cd784243e796e Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sat, 1 May 2021 07:57:08 +0200 Subject: [PATCH 11/34] [WAS MAP] Minor tweaks. Always show button. Fetches band directly from the dropdown instead. Removed unneeded call to summary. --- application/controllers/Awards.php | 3 +-- application/views/awards/was/index.php | 4 +--- application/views/interface_assets/footer.php | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/application/controllers/Awards.php b/application/controllers/Awards.php index 445b160e..bfba2d85 100644 --- a/application/controllers/Awards.php +++ b/application/controllers/Awards.php @@ -516,10 +516,9 @@ class Awards extends CI_Controller { $data['was_array'] = $this->was->get_was_array($bands, $postdata); - $data['was_summary'] = $this->was->get_was_summary($bands); $data['page_title'] = ""; - + $this->load->view('awards/was/map', $data); } } diff --git a/application/views/awards/was/index.php b/application/views/awards/was/index.php index 2d609fa6..2b5d3a1e 100644 --- a/application/views/awards/was/index.php +++ b/application/views/awards/was/index.php @@ -66,9 +66,7 @@ - input->post('band')) { ?> - ×Nothing found!'; - } \ No newline at end of file + } diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index 323e06bb..9ba26764 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -23,9 +23,9 @@ function load_was_map() { BootstrapDialog.show({ - title: 'Worked All States Map (input->post('band'); ?>)', + title: 'Worked All States Map ('+$('#band2').val()+')', cssClass: 'was-map-dialog', - message: $('
').load(site_url + '/awards/was_map/input->post('band'); ?>') + message: $('
').load(site_url + '/awards/was_map/' + $('#band2').val()) }); } From 66e26ad8cbcc8d2e6de2135a3def68b0b4763c61 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sat, 1 May 2021 09:42:32 +0200 Subject: [PATCH 12/34] [WAS MAP] Moved the map button to the right of the two other buttons. --- application/views/awards/was/index.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/views/awards/was/index.php b/application/views/awards/was/index.php index 2b5d3a1e..5531b8f3 100644 --- a/application/views/awards/was/index.php +++ b/application/views/awards/was/index.php @@ -59,14 +59,13 @@
+
- - Date: Sat, 1 May 2021 19:52:13 +0200 Subject: [PATCH 13/34] [ADIF Import] Added option to always use login call as operator. Request by OE3IDE. --- application/controllers/Adif.php | 2 +- application/controllers/Api.php | 8 ++-- application/controllers/Lotw.php | 68 ++++++++++++++-------------- application/models/Logbook_model.php | 12 +++-- application/views/adif/import.php | 9 ++++ 5 files changed, 57 insertions(+), 42 deletions(-) diff --git a/application/controllers/Adif.php b/application/controllers/Adif.php index 32282650..3864f0be 100644 --- a/application/controllers/Adif.php +++ b/application/controllers/Adif.php @@ -226,7 +226,7 @@ class adif extends CI_Controller { $custom_errors .= $this->logbook_model->import($record, $this->input->post('station_profile'), - $this->input->post('skipDuplicate'), $this->input->post('markLotw'), $this->input->post('dxccAdif'), $this->input->post('markQrz'), true); + $this->input->post('skipDuplicate'), $this->input->post('markLotw'), $this->input->post('dxccAdif'), $this->input->post('markQrz'), true, $this->input->post('operatorName')); }; diff --git a/application/controllers/Api.php b/application/controllers/Api.php index 9db17851..9e88e859 100644 --- a/application/controllers/Api.php +++ b/application/controllers/Api.php @@ -60,7 +60,7 @@ class API extends CI_Controller { function edit($key) { $this->load->model('user_model'); - + if(!$this->user_model->authorize(99)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); } $this->load->model('api_model'); @@ -426,12 +426,12 @@ class API extends CI_Controller { { break; }; - + if(isset($obj['station_profile_id'])) { - $this->logbook_model->import($record, $obj['station_profile_id'], NULL, NULL, NULL, NULL, false); + $this->logbook_model->import($record, $obj['station_profile_id'], NULL, NULL, NULL, NULL, false, false); } else { - $this->logbook_model->import($record, 0, NULL, NULL, NULL, NULL, false); + $this->logbook_model->import($record, 0, NULL, NULL, NULL, NULL, false, false); } }; diff --git a/application/controllers/Lotw.php b/application/controllers/Lotw.php index 7c0b7903..248fde4d 100644 --- a/application/controllers/Lotw.php +++ b/application/controllers/Lotw.php @@ -5,7 +5,7 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Controller: Lotw |-------------------------------------------------------------------------- - | + | | This Controller handles all things LOTW, upload and download. | | @@ -23,7 +23,7 @@ class Lotw extends CI_Controller { { parent::__construct(); $this->load->helper(array('form', 'url')); - + // Load language files $this->lang->load('lotw'); } @@ -32,7 +32,7 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Function: index |-------------------------------------------------------------------------- - | + | | Default function for the controller which loads when doing /lotw | this shows all the uploaded lotw p12 certificates the user has uploaded | @@ -65,7 +65,7 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Function: cert_upload |-------------------------------------------------------------------------- - | + | | Nothing fancy just shows the cert_upload form for uploading p12 files | */ @@ -83,14 +83,14 @@ class Lotw extends CI_Controller { // Load Views $this->load->view('interface_assets/header', $data); $this->load->view('lotw_views/upload_cert', array('error' => ' ' )); - $this->load->view('interface_assets/footer'); + $this->load->view('interface_assets/footer'); } /* |-------------------------------------------------------------------------- | Function: do_cert_upload |-------------------------------------------------------------------------- - | + | | do_cert_upload is called from cert_upload form submit and handles uploading | and processing of p12 files and storing the data into mysql | @@ -127,7 +127,7 @@ class Lotw extends CI_Controller { // Load Views $this->load->view('interface_assets/header', $data); $this->load->view('lotw_views/upload_cert', $error); - $this->load->view('interface_assets/footer'); + $this->load->view('interface_assets/footer'); } else { @@ -141,10 +141,10 @@ class Lotw extends CI_Controller { // Check DXCC & Store Country Name $this->load->model('Logbook_model'); - + if($this->input->post('dxcc') != "") { $dxcc = $this->input->post('dxcc'); - } else{ + } else{ $dxcc_check = $this->Logbook_model->check_dxcc_table($info['issued_callsign'], $info['validFrom']); $dxcc = $dxcc_check[1]; } @@ -193,7 +193,7 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Function: lotw_upload |-------------------------------------------------------------------------- - | + | | This function Uploads to LOTW | */ @@ -242,9 +242,9 @@ class Lotw extends CI_Controller { if(empty($data['qsos']->result())){ echo $station_profile->station_callsign." (".$station_profile->station_profile_name.") No QSOs to Upload
"; continue; - } + } - foreach ($data['qsos']->result() as $temp_qso) { + foreach ($data['qsos']->result() as $temp_qso) { array_push($qso_id_array, $temp_qso->COL_PRIMARY_KEY); } @@ -266,25 +266,25 @@ class Lotw extends CI_Controller { //The URL that accepts the file upload. $url = 'https://lotw.arrl.org/lotw/upload'; - + //The name of the field for the uploaded file. $uploadFieldName = 'upfile'; - + //The full path to the file that you want to upload $filePath = realpath($filename_for_saving); - + //Initiate cURL $ch = curl_init(); - + //Set the URL curl_setopt($ch, CURLOPT_URL, $url); - + //Set the HTTP request to POST curl_setopt($ch, CURLOPT_POST, true); - + //Tell cURL to return the output as a string. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - + //If the function curl_file_create exists if(function_exists('curl_file_create')){ //Use the recommended way, creating a CURLFile object. @@ -298,23 +298,23 @@ class Lotw extends CI_Controller { //starting with an @ curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false); } - + //Setup our POST fields $postFields = array( $uploadFieldName => $filePath ); - + curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); - + //Execute the request $result = curl_exec($ch); - + //If an error occured, throw an exception //with the error message. if(curl_errno($ch)){ throw new Exception(curl_error($ch)); } - + $pos = strpos($result, ""); if ($pos === false) { @@ -353,7 +353,7 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Function: delete_cert |-------------------------------------------------------------------------- - | + | | Deletes LOTW certificate from the MySQL table | */ @@ -375,7 +375,7 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Function: decrypt_key |-------------------------------------------------------------------------- - | + | | Accepts p12 file and optional password and encrypts the file returning | the required fields for LOTW and the PEM Key | @@ -429,12 +429,12 @@ class Lotw extends CI_Controller { return $data; } - + /* |-------------------------------------------------------------------------- | Function: loadFromFile |-------------------------------------------------------------------------- - | + | | $filepath is the ADIF file, $display_view is used to hide the output if its internal script | | Internal function that takes the LoTW ADIF and imports into the log @@ -498,7 +498,7 @@ class Lotw extends CI_Controller { $station_id = $this->logbook_model->find_correct_station_id($record['station_callsign'], $record['my_gridsquare']); if ($station_id != NULL) { - $result = $this->logbook_model->import($record, $station_id, NULL, TRUE, NULL, NULL, true); // Create the Entry + $result = $this->logbook_model->import($record, $station_id, NULL, TRUE, NULL, NULL, true, false); // Create the Entry if ($result == "") { $lotw_status = 'QSO imported'; } else { @@ -557,8 +557,8 @@ class Lotw extends CI_Controller { |-------------------------------------------------------------------------- | Function: lotw_download |-------------------------------------------------------------------------- - | - | Collects users with LoTW usernames and passwords and runs through them + | + | Collects users with LoTW usernames and passwords and runs through them | downloading matching QSOs. | */ @@ -834,11 +834,11 @@ class Lotw extends CI_Controller { /* Load the ARRL LOTW User Activity CSV and saves into uploads/lotw_users.csv - */ + */ public function load_users() { $contents = file_get_contents('https://lotw.arrl.org/lotw-user-activity.csv', true); - if($contents === FALSE) { + if($contents === FALSE) { echo "something went wrong"; } else { $file = './updates/lotw_users.csv'; @@ -909,7 +909,7 @@ class Lotw extends CI_Controller { return array_search(strtoupper($satname),$arr,true); } - + /* | Function: mode_map | Requires: mode as $mode, submode as $submode diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index f95f39d9..7fc8bb0a 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -1502,7 +1502,7 @@ class Logbook_model extends CI_Model { * $markQrz - used in ADIF import to mark QSOs as exported to QRZ Logbook when importing QSOs * $skipexport - used in ADIF import to skip the realtime upload to QRZ Logbook when importing QSOs from ADIF */ - function import($record, $station_id = "0", $skipDuplicate = false, $markLotw = false, $dxccAdif = false, $markQrz = false, $skipexport = false) { + function import($record, $station_id = "0", $skipDuplicate = false, $markLotw = false, $dxccAdif = false, $markQrz = false, $skipexport = false, $operatorName = false) { $CI =& get_instance(); $CI->load->library('frequency'); $my_error = ""; @@ -1805,6 +1805,12 @@ class Logbook_model extends CI_Model { } } + if ($operatorName != false) { + $operatorName = $this->session->userdata('user_callsign'); + } else { + $operatorName = (!empty($record['operator'])) ? $record['operator'] : ''; + } + // If user checked to mark QSOs as uploaded to QRZ Logbook, or else we try to find info in ADIF import. if ($markQrz != null) { $input_qrzcom_qso_upload_status = 'Y'; @@ -1918,7 +1924,7 @@ class Logbook_model extends CI_Model { 'COL_NOTES_INTL' => (!empty($record['notes_intl'])) ? $record['notes_intl'] : '', 'COL_NR_BURSTS' => (!empty($record['nr_bursts'])) ? $record['nr_bursts'] : null, 'COL_NR_PINGS' => (!empty($record['nr_pings'])) ? $record['nr_pings'] : null, - 'COL_OPERATOR' => (!empty($record['operator'])) ? $record['operator'] : '', + 'COL_OPERATOR' => $operatorName, 'COL_OWNER_CALLSIGN' => (!empty($record['owner_callsign'])) ? $record['owner_callsign'] : '', 'COL_PFX' => (!empty($record['pfx'])) ? $record['pfx'] : '', 'COL_PRECEDENCE' => (!empty($record['precedence'])) ? $record['precedence'] : '', @@ -2170,7 +2176,7 @@ class Logbook_model extends CI_Model { print("$count updated\n"); } - + public function check_missing_grid_id($all){ // get all records with no COL_GRIDSQUARE $this->db->select("COL_PRIMARY_KEY, COL_CALL, COL_TIME_ON, COL_TIME_OFF"); diff --git a/application/views/adif/import.php b/application/views/adif/import.php index 5a7cd82a..7405abdf 100644 --- a/application/views/adif/import.php +++ b/application/views/adif/import.php @@ -82,6 +82,15 @@ +
+
+
+ + +
+
+
+ From fab2759d2911d3499948cf7df6cc707642da24c0 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sun, 2 May 2021 16:06:57 +0100 Subject: [PATCH 14/34] Added new Patreons. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69843515..39d55bd6 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Thanks to Andy (VE7CXZ), Gavin (M1BXF), Graham (W5ISP), Robert (M0VFC), Corby (K Cloudlog is supported by Patreon and donations via PayPal, thanks to the following people: -Paul (M0TZO), Tim (G4VXE), Paul (N8HM), Michelle (W5NYV), Mitchell (AD0HJ), Dan (M0TCB), Martin (DK3ML), Juan Carlos (EA5WA), Iain (M0PCB), Charlie (GM1TGY), Ondrej (OK1CDJ), Trystan (G0KAY), Oliver (DL6KBG), Volkmar Schirmer, Jordan (M0PIR), Thomas Ziegler, Mathis (DB9MAT), Ken (VE3HLS), Tyler (WL7T), Jeremy Taylor, Ben Kuhn, Eric Thresher, Michael Cullen, Juuso (OH1JW), Anthony Castiglia, Fernando Ramirez-Ferrer, Robert Dixon, Mark Percival, Julia (KV1V), Timo Tomasini. +Paul (M0TZO), Tim (G4VXE), Paul (N8HM), Michelle (W5NYV), Mitchell (AD0HJ), Dan (M0TCB), Martin (DK3ML), Juan Carlos (EA5WA), Iain (M0PCB), Charlie (GM1TGY), Ondrej (OK1CDJ), Trystan (G0KAY), Oliver (DL6KBG), Volkmar Schirmer, Jordan (M0PIR), Thomas Ziegler, Mathis (DB9MAT), Ken (VE3HLS), Tyler (WL7T), Jeremy Taylor, Ben Kuhn, Eric Thresher, Michael Cullen, Juuso (OH1JW), Anthony Castiglia, Fernando Ramirez-Ferrer, Robert Dixon, Mark Percival, Julia (KV1V), Timo Tomasini, Ant (NU1U), Christopher Williams, Danny Barnes, Vic. If you'd like to donate to Cloudlog to help allow @magicbug spend less time doing commercial work and more time coding Cloudlog then you can donate via [PayPal](https://paypal.me/PGoodhall), [Github Sponsor](https://github.com/sponsors/magicbug) or become a [Patreon](https://www.patreon.com/2m0sql) From 8c166cd47b2a952c4751092cea6468639b0d2c33 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sun, 2 May 2021 16:13:06 +0100 Subject: [PATCH 15/34] Removed whitespace --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 39d55bd6..e7fb97df 100644 --- a/README.md +++ b/README.md @@ -44,5 +44,4 @@ Cloudlog is supported by Patreon and donations via PayPal, thanks to the followi Paul (M0TZO), Tim (G4VXE), Paul (N8HM), Michelle (W5NYV), Mitchell (AD0HJ), Dan (M0TCB), Martin (DK3ML), Juan Carlos (EA5WA), Iain (M0PCB), Charlie (GM1TGY), Ondrej (OK1CDJ), Trystan (G0KAY), Oliver (DL6KBG), Volkmar Schirmer, Jordan (M0PIR), Thomas Ziegler, Mathis (DB9MAT), Ken (VE3HLS), Tyler (WL7T), Jeremy Taylor, Ben Kuhn, Eric Thresher, Michael Cullen, Juuso (OH1JW), Anthony Castiglia, Fernando Ramirez-Ferrer, Robert Dixon, Mark Percival, Julia (KV1V), Timo Tomasini, Ant (NU1U), Christopher Williams, Danny Barnes, Vic. -If you'd like to donate to Cloudlog to help allow @magicbug spend less time doing commercial work and more time coding Cloudlog then you can donate via [PayPal](https://paypal.me/PGoodhall), [Github Sponsor](https://github.com/sponsors/magicbug) or become a [Patreon](https://www.patreon.com/2m0sql) - +If you'd like to donate to Cloudlog to help allow @magicbug spend less time doing commercial work and more time coding Cloudlog then you can donate via [PayPal](https://paypal.me/PGoodhall), [Github Sponsor](https://github.com/sponsors/magicbug) or become a [Patreon](https://www.patreon.com/2m0sql) \ No newline at end of file From 615ee0940212e8c5a5afdcc9f5e00738e0b10d95 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sun, 2 May 2021 16:16:14 +0100 Subject: [PATCH 16/34] [eQSL] Adding eQSL upload response to output --- application/controllers/Eqsl.php | 1 + application/views/eqsl/export.php | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/application/controllers/Eqsl.php b/application/controllers/Eqsl.php index 993bc412..bbb1c0e7 100644 --- a/application/controllers/Eqsl.php +++ b/application/controllers/Eqsl.php @@ -551,6 +551,7 @@ class eqsl extends CI_Controller { // Dump out a table with the results $data['eqsl_results_table'] = $table; + $data['eqsl_response'] = $result; } else { diff --git a/application/views/eqsl/export.php b/application/views/eqsl/export.php index 6e08311d..86f484ba 100644 --- a/application/views/eqsl/export.php +++ b/application/views/eqsl/export.php @@ -48,6 +48,11 @@ } } ?> + + +

eQSL Response:

+ + \ No newline at end of file From 50d64af400bbfa5e4b8bf71c8047a5d9fd6fa015 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sun, 2 May 2021 16:20:31 +0100 Subject: [PATCH 17/34] [eQSL] Log Upload responses as debug messages in log --- application/controllers/Eqsl.php | 2 +- application/views/eqsl/export.php | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/application/controllers/Eqsl.php b/application/controllers/Eqsl.php index bbb1c0e7..5462d8dd 100644 --- a/application/controllers/Eqsl.php +++ b/application/controllers/Eqsl.php @@ -551,7 +551,7 @@ class eqsl extends CI_Controller { // Dump out a table with the results $data['eqsl_results_table'] = $table; - $data['eqsl_response'] = $result; + log_message('debug', $result); } else { diff --git a/application/views/eqsl/export.php b/application/views/eqsl/export.php index 86f484ba..6e08311d 100644 --- a/application/views/eqsl/export.php +++ b/application/views/eqsl/export.php @@ -48,11 +48,6 @@ } } ?> - - -

eQSL Response:

- - \ No newline at end of file From 658641e51b9df2b7b8972e891a261037b3380850 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sun, 2 May 2021 17:25:59 +0200 Subject: [PATCH 18/34] [WAS Map] Added JS from this fork https://github.com/kcaran/us-map to enable labels on all states. Made a minor tweak to show popup with QSOs from clicked state. Tooltip is available, but disabled right now. --- application/models/Logbook_model.php | 14 ++-- application/views/awards/was/map.php | 114 +++++++++++++++++++++------ assets/js/jquery.usmap.min.js | 1 + 3 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 assets/js/jquery.usmap.min.js diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 7fc8bb0a..40e5db32 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -346,12 +346,14 @@ class Logbook_model extends CI_Model { $this->db->where('station_id', $station_id); $this->db->where('COL_STATE', $state); $this->db->where_in('COL_DXCC', ['291', '6', '110']); - if($band != "SAT") { - $this->db->where('COL_PROP_MODE !=', 'SAT'); - $this->db->where('COL_BAND', $band); - } else { - $this->db->where('COL_PROP_MODE', "SAT"); - } + if($band != 'All') { + if($band != "SAT") { + $this->db->where('COL_PROP_MODE !=', 'SAT'); + $this->db->where('COL_BAND', $band); + } else { + $this->db->where('COL_PROP_MODE', "SAT"); + } + } return $this->db->get($this->config->item('table_name')); } diff --git a/application/views/awards/was/map.php b/application/views/awards/was/map.php index a47d3830..1c9c7a0d 100644 --- a/application/views/awards/was/map.php +++ b/application/views/awards/was/map.php @@ -2,7 +2,7 @@ US Map Demo - + - + - - + +
+
  • Red - Not Worked
  • diff --git a/assets/js/jquery.usmap.min.js b/assets/js/jquery.usmap.min.js new file mode 100644 index 00000000..5d76a688 --- /dev/null +++ b/assets/js/jquery.usmap.min.js @@ -0,0 +1 @@ +(function($,document,window,Raphael,undefined){function jQueryPluginFactory($,name,methods,getters){getters=getters instanceof Array?getters:[];var getters_obj={};for(var i=0;i Date: Sun, 2 May 2021 22:20:50 +0100 Subject: [PATCH 19/34] [WAS][MAP] made the popup bigger --- assets/css/general.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/general.css b/assets/css/general.css index bea7aae1..2dd787b4 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -240,6 +240,6 @@ color: #ffffff; @media (min-width: 576px) { .was-map-dialog .modal-dialog { - max-width: 70% !important; + max-width: 73% !important; } } \ No newline at end of file From 9c731730b66eb2a29ca64193540e359c8f06b27e Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sun, 2 May 2021 22:27:39 +0100 Subject: [PATCH 20/34] Update map.php --- application/views/awards/was/map.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/awards/was/map.php b/application/views/awards/was/map.php index 1c9c7a0d..607bb2ad 100644 --- a/application/views/awards/was/map.php +++ b/application/views/awards/was/map.php @@ -42,7 +42,7 @@ break; } if (strpos($key, '>C<') !== false) { - echo "{fill: '#32a852'},"; + echo "{fill: '#DB4325'},"; break; } if (strpos($key, '-') !== false) { From 31b5439bc140cf288278afc0ac168f133cea9ede Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sun, 2 May 2021 22:29:20 +0100 Subject: [PATCH 21/34] Update map.php --- application/views/awards/was/map.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/views/awards/was/map.php b/application/views/awards/was/map.php index 607bb2ad..002df5c2 100644 --- a/application/views/awards/was/map.php +++ b/application/views/awards/was/map.php @@ -42,11 +42,11 @@ break; } if (strpos($key, '>C<') !== false) { - echo "{fill: '#DB4325'},"; + echo "{fill: '#32a852'},"; break; } if (strpos($key, '-') !== false) { - echo "{fill: '#e34949'},"; + echo "{fill: '#DB4325'},"; break; } } From bbdd0eadde95b3d7b0e08cd5bf6b1882d03d3d20 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Mon, 3 May 2021 14:06:56 +0100 Subject: [PATCH 22/34] [Dashboard] Adds Radio Status showing radios active in the last 15mins --- application/controllers/Dashboard.php | 6 +++++ application/models/Cat.php | 7 ++++++ application/views/dashboard/index.php | 33 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/application/controllers/Dashboard.php b/application/controllers/Dashboard.php index 2d868c50..f539f572 100644 --- a/application/controllers/Dashboard.php +++ b/application/controllers/Dashboard.php @@ -44,6 +44,12 @@ class Dashboard extends CI_Controller { $this->load->view('setup/check_list'); $this->load->view('interface_assets/footer'); } else { + + // + $this->load->model('cat'); + + $data['radio_status'] = $this->cat->recent_status(); + // Store info $data['todays_qsos'] = $this->logbook_model->todays_qsos(); $data['total_qsos'] = $this->logbook_model->total_qsos(); diff --git a/application/models/Cat.php b/application/models/Cat.php index 399cb57d..6057b8b7 100644 --- a/application/models/Cat.php +++ b/application/models/Cat.php @@ -82,6 +82,13 @@ return $query; } + function recent_status() { + $this->db->where("timestamp > date_sub(now(), interval 15 minute)", NULL, FALSE); + + $query = $this->db->get('cat'); + return $query; + } + /* Return list of radios */ function radios() { $this->db->select('id, radio'); diff --git a/application/views/dashboard/index.php b/application/views/dashboard/index.php index b8a0dcd7..9a691545 100644 --- a/application/views/dashboard/index.php +++ b/application/views/dashboard/index.php @@ -89,6 +89,39 @@
    + + num_rows()) { ?> + + + + + + + result_array() as $row) { ?> + + + + + + +
    Radio Status
    + + + + () + +
    + + + + + From 4fb2d039130b1b32ec5e744b89f3c00d1e3d826d Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Mon, 3 May 2021 14:50:33 +0100 Subject: [PATCH 23/34] [Global Options] Added the option to define the Radio Timeout Warning Radio timeout warning is used to alert users when a radio interface has disconnected and is providing stale info for logging. --- application/config/migration.php | 2 +- application/controllers/Options.php | 46 ++++++++++++++++ .../068_add_cat_timeout_to_options.php | 25 +++++++++ application/views/options/radios.php | 52 +++++++++++++++++++ application/views/options/sidebar.php | 1 + 5 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 application/migrations/068_add_cat_timeout_to_options.php create mode 100644 application/views/options/radios.php diff --git a/application/config/migration.php b/application/config/migration.php index 0768e86d..85bf1542 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -21,7 +21,7 @@ $config['migration_enabled'] = TRUE; | be upgraded / downgraded to. | */ -$config['migration_version'] = 67; +$config['migration_version'] = 68; /* |-------------------------------------------------------------------------- diff --git a/application/controllers/Options.php b/application/controllers/Options.php index 46089c45..45c7478a 100644 --- a/application/controllers/Options.php +++ b/application/controllers/Options.php @@ -99,4 +99,50 @@ class Options extends CI_Controller { } } + // function used to display the /radio url + function radio() { + + $data['page_title'] = "Cloudlog Options"; + $data['sub_heading'] = "Radio Settings"; + + $this->load->view('interface_assets/header', $data); + $this->load->view('options/radios'); + $this->load->view('interface_assets/footer'); + } + + // Handles saving the radio options to the options system. + function radio_save() { + + // Get Language Options + + $data['page_title'] = "Cloudlog Options"; + $data['sub_heading'] = "Radio Settings"; + + $this->load->helper(array('form', 'url')); + + $this->load->library('form_validation'); + + $this->form_validation->set_rules('radioTimeout', 'radioTimeout', 'required'); + + if ($this->form_validation->run() == FALSE) + { + $this->load->view('interface_assets/header', $data); + $this->load->view('options/radios'); + $this->load->view('interface_assets/footer'); + } + else + { + // Update theme choice within the options system + $radioTimeout_update = $this->optionslib->update('cat_timeout_interval', $this->input->post('radioTimeout')); + + // If theme update is complete set a flashsession with a success note + if($radioTimeout_update == TRUE) { + $this->session->set_flashdata('success', 'Radio Timeout Warning changed to '.$this->input->post('radioTimeout').' seconds'); + } + + // Redirect back to /appearance + redirect('/options/radio'); + } + } + } diff --git a/application/migrations/068_add_cat_timeout_to_options.php b/application/migrations/068_add_cat_timeout_to_options.php new file mode 100644 index 00000000..96f2f1b4 --- /dev/null +++ b/application/migrations/068_add_cat_timeout_to_options.php @@ -0,0 +1,25 @@ + "cat_timeout_interval", 'option_value' => "1800", 'autoload' => "yes"), + ); + + $this->db->insert_batch('options', $data); + } + + public function down() + { + // No option to down + } +} \ No newline at end of file diff --git a/application/views/options/radios.php b/application/views/options/radios.php new file mode 100644 index 00000000..f695993b --- /dev/null +++ b/application/views/options/radios.php @@ -0,0 +1,52 @@ +
    + +
    + + load->view('options/sidebar') ?> + + + +
    +
    +

    -

    + +
    + session->flashdata('success')) { ?> + +
    + session->flashdata('success'); ?> +
    + + + session->flashdata('message')) { ?> + +
    + session->flashdata('message'); ?> +
    + + + +
    + x + +
    + + + + +
    + +

    The Radio Timeout Warning is used on the QSO entry panel to alert you to radio interface disconnects.

    + + This number is in seconds. +
    + + + + +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/application/views/options/sidebar.php b/application/views/options/sidebar.php index 0f185c99..f7d8bdaf 100644 --- a/application/views/options/sidebar.php +++ b/application/views/options/sidebar.php @@ -2,6 +2,7 @@ \ No newline at end of file From 159aa0562d02f554b26006b801f0771341773739 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Wed, 5 May 2021 17:52:42 +0200 Subject: [PATCH 24/34] [User selectable columns] Added the possibility to configure some of the columns shown in the dashboard and in the logbook. --- application/config/migration.php | 2 +- application/controllers/Logbook.php | 64 ++++---- application/controllers/User.php | 55 ++++++- .../069_add_user_definable_columns.php | 46 ++++++ application/models/Logbook_model.php | 4 +- application/models/User_model.php | 12 ++ application/views/dashboard/index.php | 141 ++++++++++++++---- application/views/user/add.php | 56 +++++++ application/views/user/edit.php | 72 ++++++++- .../views/view_log/partial/log_ajax.php | 119 +++++++++++++-- 10 files changed, 490 insertions(+), 81 deletions(-) create mode 100644 application/migrations/069_add_user_definable_columns.php diff --git a/application/config/migration.php b/application/config/migration.php index 85bf1542..82ace1a0 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -21,7 +21,7 @@ $config['migration_enabled'] = TRUE; | be upgraded / downgraded to. | */ -$config['migration_version'] = 68; +$config['migration_version'] = 69; /* |-------------------------------------------------------------------------- diff --git a/application/controllers/Logbook.php b/application/controllers/Logbook.php index 682197f3..78ec6208 100755 --- a/application/controllers/Logbook.php +++ b/application/controllers/Logbook.php @@ -223,7 +223,7 @@ class Logbook extends CI_Controller { function worked_grid_before($gridsquare, $type, $band, $mode) { if (strlen($gridsquare) < 4) - return false; + return false; $CI =& get_instance(); $CI->load->model('Stations'); @@ -231,14 +231,14 @@ class Logbook extends CI_Controller { if($type == "SAT") { - $this->db->where('COL_PROP_MODE', 'SAT'); + $this->db->where('COL_PROP_MODE', 'SAT'); } else { - $this->db->where('COL_MODE', $mode); - $this->db->where('COL_BAND', $band); + $this->db->where('COL_MODE', $mode); + $this->db->where('COL_BAND', $band); $this->db->where('COL_PROP_MODE !=','SAT'); } - $this->db->where('station_id', $station_id); + $this->db->where('station_id', $station_id); $this->db->like('SUBSTRING(COL_GRIDSQUARE, 1, 4)', substr($gridsquare, 0, 4)); $this->db->order_by($this->config->item('table_name').".COL_TIME_ON", "desc"); $this->db->limit(1); @@ -257,7 +257,7 @@ class Logbook extends CI_Controller { /* * Function: jsonlookupgrid * - * Usage: Used to look up gridsquares when creating a QSO to check whether its needed or not + * Usage: Used to look up gridsquares when creating a QSO to check whether its needed or not * the $type variable is only used for satellites, set this to SAT. * */ @@ -271,16 +271,16 @@ class Logbook extends CI_Controller { $station_id = $CI->Stations->find_active(); if($type == "SAT") { - $this->db->where('COL_PROP_MODE', 'SAT'); + $this->db->where('COL_PROP_MODE', 'SAT'); } else { - $this->db->where('COL_MODE', $mode); - $this->db->where('COL_BAND', $band); + $this->db->where('COL_MODE', $mode); + $this->db->where('COL_BAND', $band); $this->db->where('COL_PROP_MODE !=','SAT'); } - $this->db->where('station_id', $station_id); - + $this->db->where('station_id', $station_id); + $this->db->like('SUBSTRING(COL_GRIDSQUARE, 1, 4)', substr($gridsquare, 0, 4)); $query = $this->db->get($this->config->item('table_name'), 1, 0); foreach ($query->result() as $workedBeforeRow) @@ -295,7 +295,7 @@ class Logbook extends CI_Controller { } function jsonlookupdxcc($country, $type, $band, $mode) { - + $return = [ "workedBefore" => false, ]; @@ -305,17 +305,17 @@ class Logbook extends CI_Controller { $station_id = $CI->Stations->find_active(); if($type == "SAT") { - $this->db->where('COL_PROP_MODE', 'SAT'); + $this->db->where('COL_PROP_MODE', 'SAT'); } else { - $this->db->where('COL_MODE', $mode); - $this->db->where('COL_BAND', $band); + $this->db->where('COL_MODE', $mode); + $this->db->where('COL_BAND', $band); $this->db->where('COL_PROP_MODE !=','SAT'); } - $this->db->where('station_id', $station_id); - $this->db->where('COL_COUNTRY', urldecode($country)); - + $this->db->where('station_id', $station_id); + $this->db->where('COL_COUNTRY', urldecode($country)); + $query = $this->db->get($this->config->item('table_name'), 1, 0); foreach ($query->result() as $workedBeforeRow) { @@ -329,7 +329,7 @@ class Logbook extends CI_Controller { } function jsonlookupcallsign($callsign, $type, $band, $mode) { - + // Convert - in Callsign to / Used for URL processing $callsign = str_replace("-","/",$callsign); @@ -342,17 +342,17 @@ class Logbook extends CI_Controller { $station_id = $CI->Stations->find_active(); if($type == "SAT") { - $this->db->where('COL_PROP_MODE', 'SAT'); + $this->db->where('COL_PROP_MODE', 'SAT'); } else { - $this->db->where('COL_MODE', $mode); - $this->db->where('COL_BAND', $band); + $this->db->where('COL_MODE', $mode); + $this->db->where('COL_BAND', $band); $this->db->where('COL_PROP_MODE !=','SAT'); } - $this->db->where('station_id', $station_id); - $this->db->where('COL_CALL', strtoupper($callsign)); - + $this->db->where('station_id', $station_id); + $this->db->where('COL_CALL', strtoupper($callsign)); + $query = $this->db->get($this->config->item('table_name'), 1, 0); foreach ($query->result() as $workedBeforeRow) { @@ -413,7 +413,7 @@ class Logbook extends CI_Controller { echo "]"; echo "}"; } - + function view($id) { $this->load->model('user_model'); if(!$this->user_model->authorize($this->config->item('auth_mode'))) { return; } @@ -437,11 +437,11 @@ class Logbook extends CI_Controller { $this->load->view('view_log/qso'); $this->load->view('interface_assets/footer'); } - + function partial($id) { $this->load->model('user_model'); if(!$this->user_model->authorize($this->config->item('auth_mode'))) { return; } - + $html = ""; @@ -563,12 +563,12 @@ class Logbook extends CI_Controller { if(!$this->user_model->authorize($this->config->item('auth_mode'))) { return; } - $this->db->select(''.$this->config->item('table_name').'.COL_CALL, '.$this->config->item('table_name').'.COL_BAND, '.$this->config->item('table_name').'.COL_TIME_ON, '.$this->config->item('table_name').'.COL_RST_RCVD, '.$this->config->item('table_name').'.COL_RST_SENT, '.$this->config->item('table_name').'.COL_MODE, '.$this->config->item('table_name').'.COL_SUBMODE, '.$this->config->item('table_name').'.COL_NAME, '.$this->config->item('table_name').'.COL_COUNTRY, '.$this->config->item('table_name').'.COL_PRIMARY_KEY, '.$this->config->item('table_name').'.COL_SAT_NAME, '.$this->config->item('table_name').'.COL_GRIDSQUARE, '.$this->config->item('table_name').'.COL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_SENT, '.$this->config->item('table_name').'.COL_QSL_SENT, '.$this->config->item('table_name').'.COL_STX, '.$this->config->item('table_name').'.COL_STX_STRING, '.$this->config->item('table_name').'.COL_SRX, '.$this->config->item('table_name').'.COL_SRX_STRING, '.$this->config->item('table_name').'.COL_LOTW_QSL_SENT, '.$this->config->item('table_name').'.COL_LOTW_QSL_RCVD, '.$this->config->item('table_name').'.COL_VUCC_GRIDS, station_profile.*'); - + //$this->db->select(''.$this->config->item('table_name').'.COL_CALL, '.$this->config->item('table_name').'.COL_BAND, '.$this->config->item('table_name').'.COL_TIME_ON, '.$this->config->item('table_name').'.COL_RST_RCVD, '.$this->config->item('table_name').'.COL_RST_SENT, '.$this->config->item('table_name').'.COL_MODE, '.$this->config->item('table_name').'.COL_SUBMODE, '.$this->config->item('table_name').'.COL_NAME, '.$this->config->item('table_name').'.COL_COUNTRY, '.$this->config->item('table_name').'.COL_PRIMARY_KEY, '.$this->config->item('table_name').'.COL_SAT_NAME, '.$this->config->item('table_name').'.COL_GRIDSQUARE, '.$this->config->item('table_name').'.COL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_SENT, '.$this->config->item('table_name').'.COL_QSL_SENT, '.$this->config->item('table_name').'.COL_STX, '.$this->config->item('table_name').'.COL_STX_STRING, '.$this->config->item('table_name').'.COL_SRX, '.$this->config->item('table_name').'.COL_SRX_STRING, '.$this->config->item('table_name').'.COL_LOTW_QSL_SENT, '.$this->config->item('table_name').'.COL_LOTW_QSL_RCVD, '.$this->config->item('table_name').'.COL_VUCC_GRIDS, station_profile.*'); + $this->db->from($this->config->item('table_name')); $this->db->join('station_profile', 'station_profile.station_id = '.$this->config->item('table_name').'.station_id'); - + $this->db->like(''.$this->config->item('table_name').'.COL_CALL', $id); $this->db->or_like(''.$this->config->item('table_name').'.COL_GRIDSQUARE', $id); $this->db->or_like(''.$this->config->item('table_name').'.COL_VUCC_GRIDS', $id); @@ -716,4 +716,4 @@ class Logbook extends CI_Controller { } -} \ No newline at end of file +} diff --git a/application/controllers/User.php b/application/controllers/User.php index 85bc1ce7..d47b9004 100644 --- a/application/controllers/User.php +++ b/application/controllers/User.php @@ -57,6 +57,10 @@ class User extends CI_Controller { $data['user_stylesheet'] = $this->input->post('user_stylesheet'); $data['user_sota_lookup'] = $this->input->post('user_sota_lookup'); $data['user_show_notes'] = $this->input->post('user_show_notes'); + $data['user_column1'] = $this->input->post('user_column1'); + $data['user_column2'] = $this->input->post('user_column2'); + $data['user_column3'] = $this->input->post('user_column3'); + $data['user_column4'] = $this->input->post('user_column4'); $this->load->view('user/add', $data); } else { $this->load->view('user/add', $data); @@ -65,7 +69,24 @@ class User extends CI_Controller { } else { - switch($this->user_model->add($this->input->post('user_name'), $this->input->post('user_password'), $this->input->post('user_email'), $this->input->post('user_type'), $this->input->post('user_firstname'), $this->input->post('user_lastname'), $this->input->post('user_callsign'), $this->input->post('user_locator'), $this->input->post('user_timezone'), $this->input->post('user_measurement_base'), $this->input->post('user_date_format'), $this->input->post('user_stylesheet'), $this->input->post('user_sota_lookup'), $this->input->post('user_show_notes'))) { + switch($this->user_model->add($this->input->post('user_name'), + $this->input->post('user_password'), + $this->input->post('user_email'), + $this->input->post('user_type'), + $this->input->post('user_firstname'), + $this->input->post('user_lastname'), + $this->input->post('user_callsign'), + $this->input->post('user_locator'), + $this->input->post('user_timezone'), + $this->input->post('user_measurement_base'), + $this->input->post('user_date_format'), + $this->input->post('user_stylesheet'), + $this->input->post('user_sota_lookup'), + $this->input->post('user_show_notes'), + $this->input->post('user_column1'), + $this->input->post('user_column2'), + $this->input->post('user_column3'), + $this->input->post('user_column4'))) { // Check for errors case EUSERNAMEEXISTS: $data['username_error'] = 'Username '.$this->input->post('user_name').' already in use!'; @@ -97,6 +118,10 @@ class User extends CI_Controller { $data['user_stylesheet'] = $this->input->post('user_stylesheet'); $data['user_sota_lookup'] = $this->input->post('user_sota_lookup'); $data['user_show_notes'] = $this->input->post('user_show_notes'); + $data['user_column1'] = $this->input->post('user_column1'); + $data['user_column2'] = $this->input->post('user_column2'); + $data['user_column3'] = $this->input->post('user_column3'); + $data['user_column4'] = $this->input->post('user_column4'); $this->load->view('user/add', $data); $this->load->view('interface_assets/footer'); } @@ -265,6 +290,30 @@ class User extends CI_Controller { $data['user_show_notes'] = $q->user_show_notes; } + if($this->input->post('user_column1')) { + $data['user_column1'] = $this->input->post('user_column1', true); + } else { + $data['user_column1'] = $q->user_column1; + } + + if($this->input->post('user_column2')) { + $data['user_column2'] = $this->input->post('user_column2', true); + } else { + $data['user_column2'] = $q->user_column2; + } + + if($this->input->post('user_column3')) { + $data['user_column3'] = $this->input->post('user_column3', true); + } else { + $data['user_column3'] = $q->user_column3; + } + + if($this->input->post('user_column4')) { + $data['user_column4'] = $this->input->post('user_column4', true); + } else { + $data['user_column4'] = $q->user_column4; + } + $this->load->view('user/edit', $data); $this->load->view('interface_assets/footer'); } @@ -308,6 +357,10 @@ class User extends CI_Controller { $data['user_stylesheet'] = $this->input->post('user_stylesheet'); $data['user_sota_lookup'] = $this->input->post('user_sota_lookup'); $data['user_show_notes'] = $this->input->post('user_show_notes'); + $data['user_column1'] = $this->input->post('user_column1'); + $data['user_column2'] = $this->input->post('user_column2'); + $data['user_column3'] = $this->input->post('user_column3'); + $data['user_column4'] = $this->input->post('user_column4'); $this->load->view('user/edit'); $this->load->view('interface_assets/footer'); } diff --git a/application/migrations/069_add_user_definable_columns.php b/application/migrations/069_add_user_definable_columns.php new file mode 100644 index 00000000..064269e9 --- /dev/null +++ b/application/migrations/069_add_user_definable_columns.php @@ -0,0 +1,46 @@ +dbforge->add_column('users', $fields); + + $fields = array( + 'user_column2 varchar(32) default "RSTS"', + ); + + $this->dbforge->add_column('users', $fields); + + $fields = array( + 'user_column3 varchar(32) default "RSTR"', + ); + + $this->dbforge->add_column('users', $fields); + + $fields = array( + 'user_column4 varchar(32) default "Band"', + ); + + $this->dbforge->add_column('users', $fields); + } + + public function down() + { + $this->dbforge->drop_column('users', 'user_column1'); + $this->dbforge->drop_column('users', 'user_column2'); + $this->dbforge->drop_column('users', 'user_column3'); + $this->dbforge->drop_column('users', 'user_column4'); + } +} diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 40e5db32..733dc39a 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -790,7 +790,7 @@ class Logbook_model extends CI_Model { } function get_qsos($num, $offset) { - $this->db->select(''.$this->config->item('table_name').'.COL_CALL, '.$this->config->item('table_name').'.COL_BAND, '.$this->config->item('table_name').'.COL_TIME_ON, '.$this->config->item('table_name').'.COL_RST_RCVD, '.$this->config->item('table_name').'.COL_RST_SENT, '.$this->config->item('table_name').'.COL_MODE, '.$this->config->item('table_name').'.COL_SUBMODE, '.$this->config->item('table_name').'.COL_NAME, '.$this->config->item('table_name').'.COL_COUNTRY, '.$this->config->item('table_name').'.COL_PRIMARY_KEY, '.$this->config->item('table_name').'.COL_SAT_NAME, '.$this->config->item('table_name').'.COL_GRIDSQUARE, '.$this->config->item('table_name').'.COL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_SENT, '.$this->config->item('table_name').'.COL_QSL_SENT, '.$this->config->item('table_name').'.COL_STX, '.$this->config->item('table_name').'.COL_STX_STRING, '.$this->config->item('table_name').'.COL_SRX, '.$this->config->item('table_name').'.COL_SRX_STRING, '.$this->config->item('table_name').'.COL_LOTW_QSL_SENT, '.$this->config->item('table_name').'.COL_LOTW_QSL_RCVD, '.$this->config->item('table_name').'.COL_VUCC_GRIDS, station_profile.*'); + //$this->db->select(''.$this->config->item('table_name').'.COL_CALL, '.$this->config->item('table_name').'.COL_BAND, '.$this->config->item('table_name').'.COL_TIME_ON, '.$this->config->item('table_name').'.COL_RST_RCVD, '.$this->config->item('table_name').'.COL_RST_SENT, '.$this->config->item('table_name').'.COL_MODE, '.$this->config->item('table_name').'.COL_SUBMODE, '.$this->config->item('table_name').'.COL_NAME, '.$this->config->item('table_name').'.COL_COUNTRY, '.$this->config->item('table_name').'.COL_PRIMARY_KEY, '.$this->config->item('table_name').'.COL_SAT_NAME, '.$this->config->item('table_name').'.COL_GRIDSQUARE, '.$this->config->item('table_name').'.COL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_RCVD, '.$this->config->item('table_name').'.COL_EQSL_QSL_SENT, '.$this->config->item('table_name').'.COL_QSL_SENT, '.$this->config->item('table_name').'.COL_STX, '.$this->config->item('table_name').'.COL_STX_STRING, '.$this->config->item('table_name').'.COL_SRX, '.$this->config->item('table_name').'.COL_SRX_STRING, '.$this->config->item('table_name').'.COL_LOTW_QSL_SENT, '.$this->config->item('table_name').'.COL_LOTW_QSL_RCVD, '.$this->config->item('table_name').'.COL_VUCC_GRIDS, station_profile.*'); $this->db->from($this->config->item('table_name')); $this->db->join('station_profile', 'station_profile.station_id = '.$this->config->item('table_name').'.station_id'); @@ -865,7 +865,7 @@ class Logbook_model extends CI_Model { $CI->load->model('Stations'); $station_id = $CI->Stations->find_active(); - $this->db->select('COL_CALL, COL_BAND, COL_TIME_ON, COL_RST_RCVD, COL_RST_SENT, COL_MODE, COL_SUBMODE, COL_NAME, COL_COUNTRY, COL_PRIMARY_KEY, COL_SAT_NAME, COL_STX_STRING, COL_SRX_STRING'); + //$this->db->select('COL_CALL, COL_BAND, COL_TIME_ON, COL_RST_RCVD, COL_RST_SENT, COL_MODE, COL_SUBMODE, COL_NAME, COL_COUNTRY, COL_PRIMARY_KEY, COL_SAT_NAME, COL_STX_STRING, COL_SRX_STRING, COL_IOTA, COL_STATE, COL_GRIDSQUARE'); $this->db->where("station_id", $station_id); $this->db->order_by("COL_TIME_ON", "desc"); $this->db->limit($num); diff --git a/application/models/User_model.php b/application/models/User_model.php index d2e23a09..6d5f07c4 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -114,6 +114,10 @@ class User_Model extends CI_Model { 'user_stylesheet' => xss_clean($user_stylesheet), 'user_sota_lookup' => xss_clean($user_sota_lookup), 'user_show_notes' => xss_clean($user_show_notes), + 'user_column1' => xss_clean($user_column1), + 'user_column2' => xss_clean($user_column2), + 'user_column3' => xss_clean($user_column3), + 'user_column4' => xss_clean($user_column4), ); // Check the password is valid @@ -157,6 +161,10 @@ class User_Model extends CI_Model { 'user_stylesheet' => xss_clean($fields['user_stylesheet']), 'user_sota_lookup' => xss_clean($fields['user_sota_lookup']), 'user_show_notes' => xss_clean($fields['user_show_notes']), + 'user_column1' => xss_clean($fields['user_column1']), + 'user_column2' => xss_clean($fields['user_column2']), + 'user_column3' => xss_clean($fields['user_column3']), + 'user_column4' => xss_clean($fields['user_column4']), ); // Check to see if the user is allowed to change user levels @@ -266,6 +274,10 @@ class User_Model extends CI_Model { 'user_stylesheet' => $u->row()->user_stylesheet, 'user_sota_lookup' => $u->row()->user_sota_lookup, 'user_show_notes' => $u->row()->user_show_notes, + 'user_column1' => $u->row()->user_column1, + 'user_column2' => $u->row()->user_column2, + 'user_column3' => $u->row()->user_column3, + 'user_column4' => $u->row()->user_column4, ); $this->session->set_userdata($userdata); diff --git a/application/views/dashboard/index.php b/application/views/dashboard/index.php index 9a691545..32b7dd27 100644 --- a/application/views/dashboard/index.php +++ b/application/views/dashboard/index.php @@ -40,19 +40,65 @@
    - - - - + '; + switch($this->session->userdata('user_column1')==""?'Mode':$this->session->userdata('user_column1')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo '' . + '' . + '' . + ''; + ?> - result() as $row) { ?> '; ?> - session->userdata('user_date_format')) { @@ -68,19 +114,60 @@ config->item('use_auth') && ($this->session->userdata('user_type') >= 2)) || $this->config->item('use_auth') === FALSE || ($this->config->item('show_time'))) { ?> - + - - - - COL_SAT_NAME != null) { ?> - - - - + session->userdata('user_column1')==""?'Mode':$this->session->userdata('user_column1')) { + case 'Mode': echo ''; + + switch($this->session->userdata('user_column2')==""?'RSTS':$this->session->userdata('user_column2')) { + case 'Mode': echo ''; + + switch($this->session->userdata('user_column3')==""?'RSTR':$this->session->userdata('user_column3')) { + case 'Mode': echo ''; + + switch($this->session->userdata('user_column4')==""?'Band':$this->session->userdata('user_column4')) { + case 'Mode': echo ''; + ?>
    lang->line('dashboard_qso_breakdown'); ?>lang->line('general_word_time'); ?> lang->line('gen_hamradio_call'); ?>lang->line('gen_hamradio_mode'); ?>lang->line('gen_hamradio_rst_sent'); ?>lang->line('gen_hamradio_rst_recv'); ?>lang->line('gen_hamradio_band'); ?>'; + switch($this->session->userdata('user_column2')==""?'RSTS':$this->session->userdata('user_column2')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo ''; + switch($this->session->userdata('user_column3')==""?'RSTR':$this->session->userdata('user_column3')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo ''; + switch($this->session->userdata('user_column4')==""?'Band':$this->session->userdata('user_column4')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo '
    COL_TIME_ON); echo date($custom_date_format, $timestamp); ?> COL_TIME_ON); echo date('H:i', $timestamp); ?> COL_CALL)); ?> COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; ?>COL_RST_SENT; ?> COL_STX_STRING) { ?>COL_STX_STRING;?>COL_RST_RCVD; ?> COL_SRX_STRING) { ?>COL_SRX_STRING;?>COL_SAT_NAME; ?>COL_BAND); ?>'; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo ''; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo ''; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo ''; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo '
    @@ -96,7 +183,7 @@ Radio Status - + result_array() as $row) { ?> @@ -114,8 +201,8 @@ - lang->line('dashboard_qso_breakdown'); ?> - + lang->line('general_word_total'); ?> - + lang->line('general_word_year'); ?> @@ -149,7 +236,7 @@ lang->line('dashboard_countries_breakdown'); ?> - + lang->line('general_word_worked'); ?> @@ -162,7 +249,7 @@ - + lang->line('general_word_needed'); ?> @@ -170,21 +257,21 @@ config->item('use_auth') && ($this->session->userdata('user_type') >= 2)) || $this->config->item('use_auth') === FALSE) { ?> - +
    - + - + - + diff --git a/application/views/user/add.php b/application/views/user/add.php index 64d61119..2d4d3283 100644 --- a/application/views/user/add.php +++ b/application/views/user/add.php @@ -136,6 +136,62 @@ +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    diff --git a/application/views/user/edit.php b/application/views/user/edit.php index 712310ae..bbe6b2b2 100644 --- a/application/views/user/edit.php +++ b/application/views/user/edit.php @@ -314,6 +314,76 @@
    +
    +
    +
    +
    +
    + Logbook fields +
    +
    +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    +
    +

    @@ -321,6 +391,4 @@

    - - diff --git a/application/views/view_log/partial/log_ajax.php b/application/views/view_log/partial/log_ajax.php index 0ec354f3..4e9a778f 100644 --- a/application/views/view_log/partial/log_ajax.php +++ b/application/views/view_log/partial/log_ajax.php @@ -6,10 +6,56 @@ - - - - +'; + switch($this->session->userdata('user_column1')==""?'Mode':$this->session->userdata('user_column1')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo ''; + echo ''; + echo ''; + echo ''; + ?> config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> @@ -27,7 +73,7 @@ result() as $row) { ?> - session->userdata('user_date_format')) { // If Logged in and session exists @@ -45,14 +91,55 @@ - - - - COL_SAT_NAME != null) { ?> - - - - + session->userdata('user_column1')==""?'Mode':$this->session->userdata('user_column1')) { + case 'Mode': echo ''; + switch($this->session->userdata('user_column2')==""?'RSTS':$this->session->userdata('user_column2')) { + case 'Mode': echo ''; + + switch($this->session->userdata('user_column3')==""?'RSTR':$this->session->userdata('user_column3')) { + case 'Mode': echo ''; + switch($this->session->userdata('user_column4')==""?'Band':$this->session->userdata('user_column4')) { + case 'Mode': echo ''; + ?> + config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> - + - + station_callsign)) { ?> - + config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> ' . '' . '' . ''; diff --git a/application/views/user/add.php b/application/views/user/add.php index 2d4d3283..8444f764 100644 --- a/application/views/user/add.php +++ b/application/views/user/add.php @@ -142,12 +142,12 @@ @@ -156,12 +156,12 @@ @@ -170,12 +170,12 @@ @@ -184,12 +184,12 @@ diff --git a/application/views/user/edit.php b/application/views/user/edit.php index bbe6b2b2..ff28a285 100644 --- a/application/views/user/edit.php +++ b/application/views/user/edit.php @@ -328,12 +328,12 @@ @@ -342,12 +342,12 @@ @@ -356,12 +356,12 @@ @@ -370,12 +370,12 @@ diff --git a/application/views/view_log/partial/log_ajax.php b/application/views/view_log/partial/log_ajax.php index 4e9a778f..dd46a581 100644 --- a/application/views/view_log/partial/log_ajax.php +++ b/application/views/view_log/partial/log_ajax.php @@ -10,48 +10,48 @@ echo ''; echo ''; echo ''; echo ''; From da279ffedf036a298866e6ff31321a80eecaabd7 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Thu, 6 May 2021 12:58:55 +0200 Subject: [PATCH 26/34] [User selectable columns] Added translations for the new texts added. Started a new language file for user accounts. --- application/controllers/User.php | 9 +++++++++ application/language/english/account_lang.php | 9 +++++++++ application/views/user/add.php | 8 ++++---- application/views/user/edit.php | 10 +++++----- 4 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 application/language/english/account_lang.php diff --git a/application/controllers/User.php b/application/controllers/User.php index d47b9004..ef8908c6 100644 --- a/application/controllers/User.php +++ b/application/controllers/User.php @@ -2,6 +2,15 @@ class User extends CI_Controller { + function __construct() + { + parent::__construct(); + + // Load language files + $this->lang->load(array( + 'account', + )); + } public function index() { diff --git a/application/language/english/account_lang.php b/application/language/english/account_lang.php new file mode 100644 index 00000000..22c1f3e7 --- /dev/null +++ b/application/language/english/account_lang.php @@ -0,0 +1,9 @@ +
    - + @@ -166,7 +166,7 @@
    - + diff --git a/application/views/user/edit.php b/application/views/user/edit.php index ff28a285..641c6bc4 100644 --- a/application/views/user/edit.php +++ b/application/views/user/edit.php @@ -319,12 +319,12 @@
    - Logbook fields + lang->line('account_logbook_fields'); ?>
    - + @@ -352,7 +352,7 @@
    - + From 422fbd4490d4a961a0105089437f5b7bb86fc14b Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 6 May 2021 15:34:30 +0100 Subject: [PATCH 27/34] [Frequency Library] Added hz_to_mhz function to the library as its used a lot in Cloudlog --- application/libraries/Frequency.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/application/libraries/Frequency.php b/application/libraries/Frequency.php index 5e893a36..22b7b2f3 100644 --- a/application/libraries/Frequency.php +++ b/application/libraries/Frequency.php @@ -154,5 +154,11 @@ class Frequency { } return $Band; } + + // converts a frequency in Hz to MHz output + function hz_to_mhz($frequency) + { + return number_format (($frequency / 1000 / 1000), 3) . " MHz"; + } } /* End of file Frequency.php */ From c6d2e701fe57a475e1dd261936572a3cfe52caa7 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 6 May 2021 15:36:52 +0100 Subject: [PATCH 28/34] Added Library Frequency to autoload Added frequency to autoload and changed the Radio Status to use the library. --- application/config/autoload.php | 2 +- application/views/dashboard/index.php | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/application/config/autoload.php b/application/config/autoload.php index c0530781..559d7f6c 100644 --- a/application/config/autoload.php +++ b/application/config/autoload.php @@ -52,7 +52,7 @@ $autoload['packages'] = array(APPPATH.'third_party'); | $autoload['libraries'] = array('database', 'session', 'xmlrpc'); */ -$autoload['libraries'] = array('database', 'session', 'curl', 'OptionsLib'); +$autoload['libraries'] = array('database', 'session', 'curl', 'OptionsLib', 'Frequency'); /* diff --git a/application/views/dashboard/index.php b/application/views/dashboard/index.php index c590fdda..8edb496f 100644 --- a/application/views/dashboard/index.php +++ b/application/views/dashboard/index.php @@ -191,7 +191,7 @@ - () + frequency->hz_to_mhz($row['frequency']); ?> ()
    @@ -201,14 +201,6 @@ - -
    lang->line('general_word_qslcards'); ?>
    lang->line('general_word_sent'); ?>
    lang->line('general_word_received'); ?>
    lang->line('general_word_requested'); ?> lang->line('general_word_time'); ?> lang->line('gen_hamradio_call'); ?>lang->line('gen_hamradio_mode'); ?>lang->line('gen_hamradio_rsts'); ?>lang->line('gen_hamradio_rstr'); ?>lang->line('gen_hamradio_band'); ?>'; + switch($this->session->userdata('user_column2')==""?'RSTS':$this->session->userdata('user_column2')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo ''; + switch($this->session->userdata('user_column3')==""?'RSTR':$this->session->userdata('user_column3')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo ''; + switch($this->session->userdata('user_column4')==""?'Band':$this->session->userdata('user_column4')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo 'IOTA'; break; + case 'State': echo 'State'; break; + case 'Grid': echo 'Gridsquare'; break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo 'lang->line('general_word_country'); ?> QSL
    COL_CALL)); ?> COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; ?>COL_RST_SENT; ?> COL_STX) { ?>COL_STX;?>COL_STX_STRING) { ?>COL_STX_STRING;?>COL_RST_RCVD; ?> COL_SRX) { ?>COL_SRX;?>COL_SRX_STRING) { ?>COL_SRX_STRING;?>COL_SAT_NAME; ?>COL_BAND); ?>'; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX) { echo '' . $row->COL_STX . '';}if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo ''; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX) { echo '' . $row->COL_STX . '';}if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo ''; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX) { echo '' . $row->COL_STX . '';}if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo ''; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX) { echo '' . $row->COL_STX . '';}if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo 'COL_COUNTRY))); ?> @@ -115,15 +202,15 @@ station_callsign; ?> '; switch($this->session->userdata('user_column1')==""?'Mode':$this->session->userdata('user_column1')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; switch($this->session->userdata('user_column2')==""?'RSTS':$this->session->userdata('user_column2')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; switch($this->session->userdata('user_column3')==""?'RSTR':$this->session->userdata('user_column3')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; switch($this->session->userdata('user_column4')==""?'Band':$this->session->userdata('user_column4')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ' '; switch($this->session->userdata('user_column1')==""?'Mode':$this->session->userdata('user_column1')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; switch($this->session->userdata('user_column2')==""?'RSTS':$this->session->userdata('user_column2')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; switch($this->session->userdata('user_column3')==""?'RSTR':$this->session->userdata('user_column3')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; switch($this->session->userdata('user_column4')==""?'Band':$this->session->userdata('user_column4')) { case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; - case 'RSTS': echo $this->lang->line('gen_hamradio_rst_sent'); break; - case 'RSTR': echo $this->lang->line('gen_hamradio_rst_recv'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; case 'Country': echo $this->lang->line('general_word_country'); break; - case 'IOTA': echo 'IOTA'; break; - case 'State': echo 'State'; break; - case 'Grid': echo 'Gridsquare'; break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo '
    From 9692adc75afe932f1303c550992a4495a1e761b3 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 6 May 2021 15:44:38 +0100 Subject: [PATCH 29/34] [QSO Popup] Cleaned up code --- application/views/view_log/qso.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/application/views/view_log/qso.php b/application/views/view_log/qso.php index 032b80a7..a80dafbb 100644 --- a/application/views/view_log/qso.php +++ b/application/views/view_log/qso.php @@ -70,12 +70,12 @@ config->item('display_freq') == true) { ?> - + COL_FREQ_RX != 0) { ?> - + @@ -455,11 +455,4 @@ var callsign = "COL_CALL; ?>"; - - \ No newline at end of file + \ No newline at end of file From 17c1728a03d679e50ac0b6a08178af657c724779 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Thu, 6 May 2021 16:49:33 +0100 Subject: [PATCH 30/34] Update general.css --- assets/css/general.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/css/general.css b/assets/css/general.css index 2dd787b4..77f5e3a8 100644 --- a/assets/css/general.css +++ b/assets/css/general.css @@ -238,8 +238,8 @@ color: #ffffff; width: 100%; } -@media (min-width: 576px) { - .was-map-dialog .modal-dialog { - max-width: 73% !important; - } - } \ No newline at end of file + @media (min-width: 576px) { + .was-map-dialog .modal-dialog { + max-width: 73% !important; + } + } \ No newline at end of file From de5c65c121a707fb1cb065e7b722cbaba9b6f0c2 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Fri, 7 May 2021 08:03:25 +0200 Subject: [PATCH 31/34] [User selectable columns] Added a fifth column, used only in logbook view. Also fixed a bug when adding user accounts. --- application/config/migration.php | 2 +- application/controllers/User.php | 13 +++++++++- application/language/english/account_lang.php | 1 + .../migrations/070_add_fifth_column.php | 25 +++++++++++++++++++ application/models/User_model.php | 7 +++++- application/views/user/add.php | 15 +++++++++++ application/views/user/edit.php | 13 ++++++++++ .../views/view_log/partial/log_ajax.php | 17 ++++++++++--- 8 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 application/migrations/070_add_fifth_column.php diff --git a/application/config/migration.php b/application/config/migration.php index 82ace1a0..5b216f92 100644 --- a/application/config/migration.php +++ b/application/config/migration.php @@ -21,7 +21,7 @@ $config['migration_enabled'] = TRUE; | be upgraded / downgraded to. | */ -$config['migration_version'] = 69; +$config['migration_version'] = 70; /* |-------------------------------------------------------------------------- diff --git a/application/controllers/User.php b/application/controllers/User.php index ef8908c6..976ea38e 100644 --- a/application/controllers/User.php +++ b/application/controllers/User.php @@ -70,6 +70,7 @@ class User extends CI_Controller { $data['user_column2'] = $this->input->post('user_column2'); $data['user_column3'] = $this->input->post('user_column3'); $data['user_column4'] = $this->input->post('user_column4'); + $data['user_column5'] = $this->input->post('user_column5'); $this->load->view('user/add', $data); } else { $this->load->view('user/add', $data); @@ -95,7 +96,8 @@ class User extends CI_Controller { $this->input->post('user_column1'), $this->input->post('user_column2'), $this->input->post('user_column3'), - $this->input->post('user_column4'))) { + $this->input->post('user_column4'), + $this->input->post('user_column5'))) { // Check for errors case EUSERNAMEEXISTS: $data['username_error'] = 'Username '.$this->input->post('user_name').' already in use!'; @@ -131,6 +133,7 @@ class User extends CI_Controller { $data['user_column2'] = $this->input->post('user_column2'); $data['user_column3'] = $this->input->post('user_column3'); $data['user_column4'] = $this->input->post('user_column4'); + $data['user_column5'] = $this->input->post('user_column5'); $this->load->view('user/add', $data); $this->load->view('interface_assets/footer'); } @@ -323,6 +326,12 @@ class User extends CI_Controller { $data['user_column4'] = $q->user_column4; } + if($this->input->post('user_column5')) { + $data['user_column5'] = $this->input->post('user_column5', true); + } else { + $data['user_column5'] = $q->user_column5; + } + $this->load->view('user/edit', $data); $this->load->view('interface_assets/footer'); } @@ -370,6 +379,8 @@ class User extends CI_Controller { $data['user_column2'] = $this->input->post('user_column2'); $data['user_column3'] = $this->input->post('user_column3'); $data['user_column4'] = $this->input->post('user_column4'); + $data['user_column4'] = $this->input->post('user_column4'); + $data['user_column5'] = $this->input->post('user_column5'); $this->load->view('user/edit'); $this->load->view('interface_assets/footer'); } diff --git a/application/language/english/account_lang.php b/application/language/english/account_lang.php index 22c1f3e7..7db5d0ed 100644 --- a/application/language/english/account_lang.php +++ b/application/language/english/account_lang.php @@ -7,3 +7,4 @@ $lang['account_column1_text'] = 'Choose column 1'; $lang['account_column2_text'] = 'Choose column 2'; $lang['account_column3_text'] = 'Choose column 3'; $lang['account_column4_text'] = 'Choose column 4'; +$lang['account_column5_text'] = 'Choose column 5 (only for logbook)'; diff --git a/application/migrations/070_add_fifth_column.php b/application/migrations/070_add_fifth_column.php new file mode 100644 index 00000000..745469c7 --- /dev/null +++ b/application/migrations/070_add_fifth_column.php @@ -0,0 +1,25 @@ +dbforge->add_column('users', $fields); + } + + public function down() + { + $this->dbforge->drop_column('users', 'user_column5'); + } +} diff --git a/application/models/User_model.php b/application/models/User_model.php index 6d5f07c4..0652d707 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -96,7 +96,9 @@ class User_Model extends CI_Model { // FUNCTION: bool add($username, $password, $email, $type) // Add a user - function add($username, $password, $email, $type, $firstname, $lastname, $callsign, $locator, $timezone, $measurement, $user_date_format, $user_stylesheet, $user_sota_lookup, $user_show_notes) { + function add($username, $password, $email, $type, $firstname, $lastname, $callsign, $locator, $timezone, + $measurement, $user_date_format, $user_stylesheet, $user_sota_lookup, $user_show_notes, + $user_column1, $user_column2, $user_column3, $user_column4, $user_column5) { // Check that the user isn't already used if(!$this->exists($username)) { $data = array( @@ -118,6 +120,7 @@ class User_Model extends CI_Model { 'user_column2' => xss_clean($user_column2), 'user_column3' => xss_clean($user_column3), 'user_column4' => xss_clean($user_column4), + 'user_column5' => xss_clean($user_column5), ); // Check the password is valid @@ -165,6 +168,7 @@ class User_Model extends CI_Model { 'user_column2' => xss_clean($fields['user_column2']), 'user_column3' => xss_clean($fields['user_column3']), 'user_column4' => xss_clean($fields['user_column4']), + 'user_column5' => xss_clean($fields['user_column5']), ); // Check to see if the user is allowed to change user levels @@ -278,6 +282,7 @@ class User_Model extends CI_Model { 'user_column2' => $u->row()->user_column2, 'user_column3' => $u->row()->user_column3, 'user_column4' => $u->row()->user_column4, + 'user_column5' => $u->row()->user_column5, ); $this->session->set_userdata($userdata); diff --git a/application/views/user/add.php b/application/views/user/add.php index 74aadd86..7157053f 100644 --- a/application/views/user/add.php +++ b/application/views/user/add.php @@ -191,6 +191,21 @@ + + +
    + + +
    diff --git a/application/views/user/edit.php b/application/views/user/edit.php index 641c6bc4..3b879e71 100644 --- a/application/views/user/edit.php +++ b/application/views/user/edit.php @@ -378,6 +378,19 @@ +
    + + +
    diff --git a/application/views/view_log/partial/log_ajax.php b/application/views/view_log/partial/log_ajax.php index dd46a581..ee0a92c4 100644 --- a/application/views/view_log/partial/log_ajax.php +++ b/application/views/view_log/partial/log_ajax.php @@ -55,9 +55,20 @@ case 'Band': echo $this->lang->line('gen_hamradio_band'); break; } echo ''; - ?> -
    - config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> + echo ''; + + if(($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> session->userdata('user_eqsl_name') != "") { ?> From f0ec15b1eef8768bb2851f0824e1c4cefe43d4e1 Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Fri, 7 May 2021 08:18:45 +0200 Subject: [PATCH 32/34] [User selectable columns] Gridsquare fix. Now shows COL_VUCC_GRIDS if COL_GRIDSQUARE is empty/null --- application/views/dashboard/index.php | 8 ++++---- application/views/view_log/partial/log_ajax.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/application/views/dashboard/index.php b/application/views/dashboard/index.php index 8edb496f..0baec27e 100644 --- a/application/views/dashboard/index.php +++ b/application/views/dashboard/index.php @@ -126,7 +126,7 @@ case 'RSTR': echo ''; - ?> - - - config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?> + switch($this->session->userdata('user_column5')==""?'Country':$this->session->userdata('user_column5')) { + case 'Mode': echo ''; + if(($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?>
    lang->line('dashboard_qso_breakdown'); ?>
    lang->line('gen_hamradio_frequency'); ?>COL_FREQ); ?>frequency->hz_to_mhz($row->COL_FREQ); ?>
    lang->line('gen_hamradio_frequency_rx'); ?>COL_FREQ_RX); ?>frequency->hz_to_mhz($row->COL_FREQ_RX); ?>
    lang->line('general_word_country'); ?>'; + switch($this->session->userdata('user_column5')==""?'Country':$this->session->userdata('user_column5')) { + case 'Mode': echo $this->lang->line('gen_hamradio_mode'); break; + case 'RSTS': echo $this->lang->line('gen_hamradio_rsts'); break; + case 'RSTR': echo $this->lang->line('gen_hamradio_rstr'); break; + case 'Country': echo $this->lang->line('general_word_country'); break; + case 'IOTA': echo $this->lang->line('gen_hamradio_iota'); break; + case 'State': echo $this->lang->line('gen_hamradio_state'); break; + case 'Grid': echo $this->lang->line('gen_hamradio_gridsquare'); break; + case 'Band': echo $this->lang->line('gen_hamradio_band'); break; + } + echo 'QSL eQSL' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } @@ -138,7 +138,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } @@ -150,7 +150,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } @@ -162,7 +162,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } diff --git a/application/views/view_log/partial/log_ajax.php b/application/views/view_log/partial/log_ajax.php index ee0a92c4..13dcd748 100644 --- a/application/views/view_log/partial/log_ajax.php +++ b/application/views/view_log/partial/log_ajax.php @@ -110,7 +110,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } @@ -121,7 +121,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } @@ -133,7 +133,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } @@ -144,7 +144,7 @@ case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; case 'IOTA': echo '' . ($row->COL_IOTA); break; - case 'Grid': echo '' . ($row->COL_GRIDSQUARE); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; case 'State': echo '' . ($row->COL_STATE); break; } From bf1e42bd2b36058b100444420148d68258384ffc Mon Sep 17 00:00:00 2001 From: Andreas <6977712+AndreasK79@users.noreply.github.com> Date: Sat, 8 May 2021 10:37:33 +0200 Subject: [PATCH 33/34] [User selectable columns] Need to change the content as well. The title was correct. --- application/views/view_log/partial/log_ajax.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/application/views/view_log/partial/log_ajax.php b/application/views/view_log/partial/log_ajax.php index 13dcd748..49b937ec 100644 --- a/application/views/view_log/partial/log_ajax.php +++ b/application/views/view_log/partial/log_ajax.php @@ -149,10 +149,18 @@ case 'State': echo '' . ($row->COL_STATE); break; } echo 'COL_COUNTRY))); ?>'; echo $row->COL_SUBMODE==null?$row->COL_MODE:$row->COL_SUBMODE; break; + case 'RSTS': echo '' . $row->COL_RST_SENT; if ($row->COL_STX) { echo '' . $row->COL_STX . '';}if ($row->COL_STX_STRING) { echo '' . $row->COL_STX_STRING . '';}; break; + case 'RSTR': echo '' . $row->COL_RST_RCVD; if ($row->COL_SRX) { echo '' . $row->COL_SRX . '';}if ($row->COL_SRX_STRING) { echo '' . $row->COL_SRX_STRING . '';}; break; + case 'Country': echo '' . ucwords(strtolower(($row->COL_COUNTRY)));; break; + case 'IOTA': echo '' . ($row->COL_IOTA); break; + case 'Grid': echo ''; echo strlen($row->COL_GRIDSQUARE)==0?$row->COL_VUCC_GRIDS:$row->COL_GRIDSQUARE; break; + case 'Band': echo ''; if($row->COL_SAT_NAME != null) { echo $row->COL_SAT_NAME; } else { echo strtolower($row->COL_BAND); }; break; + case 'State': echo '' . ($row->COL_STATE); break; + } + echo '