diff --git a/ChangeLog b/ChangeLog index e375234afb7..72a2f1825be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,86 @@ +0.9.0 +===== + +Features / Improvements: + +81e6ba2 Mon, 1 Jun 2015 11:26:57 +0200 redesign of refresh-widget in top/bottom-navbars. Looks like Pagejumps now +3fcdf23 Sun, 24 May 2015 21:16:24 +0200 Added optional parameter send_on_finish to slide +fd25012 Tue, 28 Apr 2015 14:00:35 +0200 adding possibility to define a path for a pagejump and a scope where the pagejump is marked as active +a4f9d9b Sun, 26 Apr 2015 19:38:38 +0200 Make all widgets use strict +5743280 Sun, 26 Apr 2015 17:55:51 +0200 New feature: popup eventsThis will call a "close" event when a popup is being clicked.This close event can be also called externally to close a popup.Or you can listen externally to a close event for your own actions at that time. E.g. a diagram could listen to it to stop data fetching. +af91486 Thu, 9 Apr 2015 08:55:43 +0200 renamed infowidget to widgetinfo and actionwidget to widgetaction +0e80cd0 Sun, 29 Mar 2015 17:51:48 +0200 Make check_config a bit more robust against badly formed files. +32eaca3 Wed, 25 Mar 2015 16:59:06 +0100 - plugin infoaction added (combine a info and an action widget) - pagejumps augmented to hold an info-widget +c580956 Sun, 22 Mar 2015 13:04:20 +0100 - new client for openHAB2: using Server Side Events (SSE) +e1d753c Mon, 16 Mar 2015 08:45:21 +0100 add support for openHAB DateTime items +663f909 Sun, 1 Mar 2015 18:14:27 +0100 Update to latest icon set +1047ae7 Sun, 1 Mar 2015 17:54:57 +0100 Two changes: - Remove firstdata event as nobody seems to use it. - Improve profiling by counting read-retries and supporting the browser profiling (add to URL: ...&profile=browser) +7c0f8b9 Thu, 19 Feb 2015 22:46:25 +0100 Change make-iconhandler to respect the split of the icon config into iconconfig.js. Add latest kuf icons +af826e6 Fri, 13 Feb 2015 22:28:59 +0100 Remove obsolete yepnope +8715aee Mon, 9 Feb 2015 21:56:46 +0100 Added manoficons +f9cc09a Sun, 1 Feb 2015 15:18:36 +0100 Textify CV to improve performance +546809d Sun, 18 Jan 2015 18:07:53 +0000 New "feature": profiling instrumentation + +Bug fixes: + +af9ec61 Sun, 1 Nov 2015 22:52:34 +0100 Fix editor documentation / complex mode for webKit browsers +9245fcf Sun, 1 Nov 2015 18:08:15 +0100 added missing session to write-command +3e689f5 Sun, 1 Nov 2015 18:04:39 +0100 Fix wrong slider position due to wrong number type. +6750762 Wed, 28 Oct 2015 17:51:03 +0100 fixed wrong comment in css +38b2bd9 Mon, 12 Oct 2015 23:14:00 +0200 Fixed icon update Script to work with github and updated knx-uf-iconset +8deecc4 Mon, 12 Oct 2015 23:13:43 +0200 Fixed missing var declaration and pragmatic solution regarding OH integration +1499862 Mon, 21 Sep 2015 11:48:43 +0200 Fix position when slider is updatd while hidden +a2e94b0 Fri, 29 May 2015 10:32:51 +0200 - fix for updates in diagram_info (also fixes missing default mapping in diagram_info) see:http://knx-user-forum.de/forum/supportforen/cometvisu/829798-darstellungsprobleme-mit-git-version?p=835404#post835404 +4689fd1 Sat, 23 May 2015 23:46:36 +0200 Fix refresh widgetThe value attribute was not respected (cf. report at http://knx-user-forum.de/forum/supportforen/cometvisu/829798-darstellungsprobleme-mit-git-version?p=833726#post833726) +34fdea0 Thu, 14 May 2015 18:13:04 +0200 Fix wgplugin_infoBug: values didn't get shownFeature: use default value handling as this allows styling and mapping +449883a Thu, 7 May 2015 09:30:26 +0200 fix for height calculation on initial loading of visu (bottom navbar wasn´t visible) +6466f92 Thu, 7 May 2015 09:05:01 +0200 reenable scope attribute for navbars +4fdef6b Sat, 25 Apr 2015 00:03:43 +0200 Fix pagejump active state +a016e79 Fri, 24 Apr 2015 18:09:04 +0200 Fix diagrams not redrawing after popup open+close +540bb61 Fri, 24 Apr 2015 13:42:35 +0200 Fix diagrams not updating properly +9d96284 Sat, 11 Apr 2015 12:08:35 +0200 fixes #162 axis-config was ignored completely +b654b70 Sat, 11 Apr 2015 11:25:42 +0200 fixes #163 +75c77e2 Fri, 10 Apr 2015 10:45:08 +0200 - fix for clock-plugin dragging - fix for OH:date(time) transform handling +13b8939 Fri, 10 Apr 2015 08:50:14 +0200 - added default mapping to metal-config - fix for default value handling (fixes #153) +08334d3 Fri, 10 Apr 2015 08:50:01 +0200 fixes #152 +595e6d4 Thu, 9 Apr 2015 17:47:46 +0200 separate addresses for up/down action possible +15c3e7f Thu, 9 Apr 2015 14:20:09 +0200 layout handlung fixed in SVG plugin +ddb4f2e Thu, 9 Apr 2015 14:13:10 +0200 change svg-plugin creator to return string: fixes #151 +8f3ec4a Thu, 9 Apr 2015 09:49:55 +0200 - added default mapping to metal-config - fix for default value handling (fixes #153) +0c549da Thu, 9 Apr 2015 09:14:26 +0200 fixes #152 +c580956 Sun, 22 Mar 2015 13:04:20 +0100 - Bugfixes for the color-transform calculation from HSV to RGB - Bugfixes for RGB-mode in colorshooser - changed XSD because colorchooser can work with only one address (which sends/reads rgb data) +4ca3660 Mon, 16 Mar 2015 11:55:12 +0100 fix for sliders with min>0 +b1f86bd Sun, 15 Mar 2015 12:35:02 +0100 fix for slides without value +cabacdd Fri, 13 Mar 2015 18:27:33 +0100 - fix for correct placement of the horizontal slider handle +7ba196b Sun, 1 Mar 2015 17:13:53 +0100 Fix display / hide of navbar during setup time +166f53e Sun, 1 Mar 2015 16:24:18 +0100 Differ in handling of touch+move by switching to scroll mode and stopping action mode. +291a17c Sat, 28 Feb 2015 19:46:11 +0100 Fix active marking for pagejump in the navbars +ef1079c Sat, 28 Feb 2015 18:48:32 +0100 Fix to respect size when raw numbers (instead ob number + 'px') were given +9a09a85 Thu, 26 Feb 2015 22:40:13 +0100 Test fix for http://knx-user-forum.de/467025-post87.html +e325f08 Sun, 22 Feb 2015 00:10:31 +0100 Possible fix for http://knx-user-forum.de/465733-post60.html +a971317 Fri, 20 Feb 2015 18:15:05 +0100 Fix for http://knx-user-forum.de/cometvisu/41197-dev-mapping-icon-wird-nicht-geladen.html +5e275c9 Tue, 17 Feb 2015 14:42:18 +0100 fix for automatic column adjustment inside groups +403254d Sun, 15 Feb 2015 22:56:59 +0100 Fix for missing elements on a page end +adf4ab2 Sun, 15 Feb 2015 22:39:50 +0100 Fix http://knx-user-forum.de/463835-post47.html +1a541d3 Sun, 15 Feb 2015 17:10:29 +0100 Fix page switch by GA +a03210f Thu, 12 Feb 2015 21:35:53 +0100 Fix for http://knx-user-forum.de/462242-post39.html +825b75c Sun, 8 Feb 2015 22:33:38 +0100 Fix for https://github.com/CometVisu/CometVisu/issues/17 +feedc4e Thu, 29 Jan 2015 17:47:22 +0100 fixes missing inheritance of navbar visibility +6d242ee Wed, 28 Jan 2015 17:47:07 +0100 changed bind_click_to_widget to always true for pagejumps, otherwise some strange CSS tricks are necessary to get pagejumps working in navbars and in pages at the same time +28733c5 Sun, 25 Jan 2015 16:48:01 +0100 rowspan fix +6f416c3 Sun, 25 Jan 2015 12:02:03 +0000 Fix for pagejumps to pages with not unique names: take the ancestor/descendant of th current page if there is one +f6b02ff Sun, 25 Jan 2015 11:02:34 +0000 fix for showtopnavigation=false on subpages +a4edf94 Sat, 24 Jan 2015 23:10:24 +0000 Fix column display in the navbars +95875b4 Sat, 24 Jan 2015 20:31:20 +0000 Switch from absolute IDs to Page Names +ffc6785 Sat, 24 Jan 2015 20:01:19 +0000 Fix for http://knx-user-forum.de/cometvisu/40537-visu_config-xsd-existiert-nicht.html +a345ab1 Sat, 24 Jan 2015 10:22:07 +0000 bugfix Gauge plugin: adapt to new data hash +c0d3e60 Sat, 24 Jan 2015 09:59:37 +0000 - fixes correct amount of columns in groups - some date for the root page was misplaced and therefore a couple of parameters could not be found (showtopnavigation, etc.) +9c95111 Fri, 23 Jan 2015 21:22:10 +0000 Fix pagejumps where the ID had changed +dacd7b5 Fri, 23 Jan 2015 13:55:45 +0000 fix for pagejumps that are not part of navbars +7bb098c Fri, 16 Jan 2015 22:53:18 +0000 Bugfix and performance improvement for big configurations: only read GAs where the read flag is set (i.e. ignore write only GAs) +9954d67 Thu, 15 Jan 2015 18:03:55 +0000 only query the skipped addresses in second request when enableQueue is active +acc8c18 Thu, 15 Jan 2015 15:40:50 +0000 Fix for enableAddressQueue parameter, which loads the states on the start page first + 0.8.5 ===== diff --git a/README b/README index c53b3eb0b2d..d0766ceea6c 100644 --- a/README +++ b/README @@ -34,4 +34,5 @@ as it will contain the latest information. To discuss with the developers you can use the mailing list that is provided at the SourceForge project page or use the KNX User Forum -at http://knx-user-forum.de/cometvisu/ (spoken languages are German and English) +at http://knx-user-forum.de/forum/supportforen/cometvisu/ (spoken languages are +German and English) diff --git a/build.js b/build.js index af14c3a586c..40f3620f875 100644 --- a/build.js +++ b/build.js @@ -10,8 +10,8 @@ separateCSS: true, buildCSS: false, paths: { - 'css-builder': '../../../_support/css-builder', - 'normalize': '../../../_support/normalize' + 'css-builder': '../build/css-builder', + 'normalize': '../build/normalize' }, modules: [ diff --git a/build/css-builder.js b/build/css-builder.js new file mode 100644 index 00000000000..e2bd36a5c4a --- /dev/null +++ b/build/css-builder.js @@ -0,0 +1,200 @@ +define(['require', './normalize'], function(req, normalize) { + var cssAPI = {}; + + var isWindows = !!process.platform.match(/^win/); + + function compress(css) { + if (config.optimizeCss == 'none') { + return css; + } + if (typeof process !== "undefined" && process.versions && !!process.versions.node && require.nodeRequire) { + try { + var csso = require.nodeRequire('csso'); + } + catch(e) { + console.log('Compression module not installed. Use "npm install csso -g" to enable.'); + return css; + } + var csslen = css.length; + try { + css = csso.justDoIt(css); + } + catch(e) { + console.log('Compression failed due to a CSS syntax error.'); + return css; + } + console.log('Compressed CSS output to ' + Math.round(css.length / csslen * 100) + '%.'); + return css; + } + console.log('Compression not supported outside of nodejs environments.'); + return css; + } + + //load file code - stolen from text plugin + function loadFile(path) { + if (typeof process !== "undefined" && process.versions && !!process.versions.node && require.nodeRequire) { + var fs = require.nodeRequire('fs'); + var file = fs.readFileSync(path, 'utf8'); + if (file.indexOf('\uFEFF') === 0) + return file.substring(1); + return file; + } + else { + var file = new java.io.File(path), + lineSeparator = java.lang.System.getProperty("line.separator"), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), 'utf-8')), + stringBuffer, line; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + if (line && line.length() && line.charAt(0) === 0xfeff) + line = line.substring(1); + stringBuffer.append(line); + while ((line = input.readLine()) !== null) { + stringBuffer.append(lineSeparator).append(line); + } + return String(stringBuffer.toString()); + } + finally { + input.close(); + } + } + } + + + function saveFile(path, data) { + if (typeof process !== "undefined" && process.versions && !!process.versions.node && require.nodeRequire) { + var fs = require.nodeRequire('fs'); + fs.writeFileSync(path, data, 'utf8'); + } + else { + var content = new java.lang.String(data); + var output = new java.io.BufferedWriter(new java.io.OutputStreamWriter(new java.io.FileOutputStream(path), 'utf-8')); + + try { + output.write(content, 0, content.length()); + output.flush(); + } + finally { + output.close(); + } + } + } + + //when adding to the link buffer, paths are normalised to the baseUrl + //when removing from the link buffer, paths are normalised to the output file path + function escape(content) { + return content.replace(/(["'\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r"); + } + + // NB add @media query support for media imports + var importRegEx = /@import\s*(url)?\s*(('([^']*)'|"([^"]*)")|\(('([^']*)'|"([^"]*)"|([^\)]*))\))\s*;?/g; + var absUrlRegEx = /^([^\:\/]+:\/)?\//; + + + var siteRoot; + + var baseParts = req.toUrl('base_url').split('/'); + baseParts[baseParts.length - 1] = ''; + var baseUrl = baseParts.join('/'); + + var curModule = 0; + var config; + + var layerBuffer = []; + var cssBuffer = {}; + + cssAPI.load = function(name, req, load, _config) { + + //store config + config = config || _config; + + if (!siteRoot) { + siteRoot = path.resolve(config.dir || path.dirname(config.out), config.siteRoot || '.') + '/'; + if (isWindows) + siteRoot = siteRoot.replace(/\\/g, '/'); + } + + //external URLS don't get added (just like JS requires) + if (name.match(absUrlRegEx)) + return load(); + + var fileUrl = req.toUrl(name + '.css'); + if (isWindows) + fileUrl = fileUrl.replace(/\\/g, '/'); + + // rebase to the output directory if based on the source directory; + // baseUrl points always to the output directory, fileUrl only if + // it is not prefixed by a computed path (relative too) + var fileSiteUrl = fileUrl; + if (fileSiteUrl.indexOf(baseUrl) < 0) { + var appRoot = req.toUrl(config.appDir); + if (isWindows) + appRoot = appRoot.replace(/\\/g, '/'); + if (fileSiteUrl.indexOf(appRoot) == 0) + fileSiteUrl = siteRoot + fileSiteUrl.substring(appRoot.length); + } + + //add to the buffer + cssBuffer[name] = normalize(loadFile(fileUrl), fileSiteUrl, siteRoot); + + load(); + } + + cssAPI.normalize = function(name, normalize) { + if (name.substr(name.length - 4, 4) == '.css') + name = name.substr(0, name.length - 4); + return normalize(name); + } + + cssAPI.write = function(pluginName, moduleName, write, parse) { + //external URLS don't get added (just like JS requires) + if (moduleName.match(absUrlRegEx)) + return; + + layerBuffer.push(cssBuffer[moduleName]); + + if (config.buildCSS != false) + write.asModule(pluginName + '!' + moduleName, 'define(function(){})'); + } + + cssAPI.onLayerEnd = function(write, data) { + if (config.separateCSS && config.IESelectorLimit) + throw 'RequireCSS: separateCSS option is not compatible with ensuring the IE selector limit'; + + if (config.separateCSS) { + var outPath = data.path.replace(/(\.js)?$/, '.css'); + console.log('Writing CSS! file: ' + outPath + '\n'); + + var css = layerBuffer.join(''); + + process.nextTick(function() { + if (fs.existsSync(outPath)) { + css = css + fs.readFileSync(outPath, {encoding: 'utf8'}); + } + saveFile(outPath, compress(css)); + }); + + } + else if (config.buildCSS != false) { + var styles = config.IESelectorLimit ? layerBuffer : [layerBuffer.join('')]; + for (var i = 0; i < styles.length; i++) { + if (styles[i] == '') + return; + write( + "(function(c){var d=document,a='appendChild',i='styleSheet',s=d.createElement('style');s.type='text/css';d.getElementsByTagName('head')[0][a](s);s[i]?s[i].cssText=c:s[a](d.createTextNode(c));})\n" + + "('" + escape(compress(styles[i])) + "');\n" + ); + } + } + //clear layer buffer for next layer + layerBuffer = []; + } + + return cssAPI; +}); diff --git a/build/normalize.js b/build/normalize.js new file mode 100644 index 00000000000..bca139463d1 --- /dev/null +++ b/build/normalize.js @@ -0,0 +1,141 @@ +//>>excludeStart('excludeRequireCss', pragmas.excludeRequireCss) +/* + * css.normalize.js + * + * CSS Normalization + * + * CSS paths are normalized based on an optional basePath and the RequireJS config + * + * Usage: + * normalize(css, fromBasePath, toBasePath); + * + * css: the stylesheet content to normalize + * fromBasePath: the absolute base path of the css relative to any root (but without ../ backtracking) + * toBasePath: the absolute new base path of the css relative to the same root + * + * Absolute dependencies are left untouched. + * + * Urls in the CSS are picked up by regular expressions. + * These will catch all statements of the form: + * + * url(*) + * url('*') + * url("*") + * + * @import '*' + * @import "*" + * + * (and so also @import url(*) variations) + * + * For urls needing normalization + * + */ + +define(function() { + + // regular expression for removing double slashes + // eg http://www.example.com//my///url/here -> http://www.example.com/my/url/here + var slashes = /([^:])\/+/g + var removeDoubleSlashes = function(uri) { + return uri.replace(slashes, '$1/'); + } + + // given a relative URI, and two absolute base URIs, convert it from one base to another + var protocolRegEx = /[^\:\/]*:\/\/([^\/])*/; + var absUrlRegEx = /^(\/|data:)/; + function convertURIBase(uri, fromBase, toBase) { + if (uri.match(absUrlRegEx) || uri.match(protocolRegEx)) + return uri; + uri = removeDoubleSlashes(uri); + // if toBase specifies a protocol path, ensure this is the same protocol as fromBase, if not + // use absolute path at fromBase + var toBaseProtocol = toBase.match(protocolRegEx); + var fromBaseProtocol = fromBase.match(protocolRegEx); + if (fromBaseProtocol && (!toBaseProtocol || toBaseProtocol[1] != fromBaseProtocol[1] || toBaseProtocol[2] != fromBaseProtocol[2])) + return absoluteURI(uri, fromBase); + + else { + return relativeURI(absoluteURI(uri, fromBase), toBase); + } + }; + + // given a relative URI, calculate the absolute URI + function absoluteURI(uri, base) { + if (uri.substr(0, 2) == './') + uri = uri.substr(2); + + // absolute urls are left in tact + if (uri.match(absUrlRegEx) || uri.match(protocolRegEx)) + return uri; + + var baseParts = base.split('/'); + var uriParts = uri.split('/'); + + baseParts.pop(); + + while (curPart = uriParts.shift()) + if (curPart == '..') + baseParts.pop(); + else + baseParts.push(curPart); + + return baseParts.join('/'); + }; + + + // given an absolute URI, calculate the relative URI + function relativeURI(uri, base) { + + // reduce base and uri strings to just their difference string + var baseParts = base.split('/'); + baseParts.pop(); + base = baseParts.join('/') + '/'; + i = 0; + while (base.substr(i, 1) == uri.substr(i, 1)) + i++; + while (base.substr(i, 1) != '/') + i--; + base = base.substr(i + 1); + uri = uri.substr(i + 1); + + // each base folder difference is thus a backtrack + baseParts = base.split('/'); + var uriParts = uri.split('/'); + out = ''; + while (baseParts.shift()) + out += '../'; + + // finally add uri parts + while (curPart = uriParts.shift()) + out += curPart + '/'; + + return out.substr(0, out.length - 1); + }; + + var normalizeCSS = function(source, fromBase, toBase) { + + fromBase = removeDoubleSlashes(fromBase); + toBase = removeDoubleSlashes(toBase); + + var urlRegEx = /@import\s*("([^"]*)"|'([^']*)')|url\s*\((?!#)\s*(\s*"([^"]*)"|'([^']*)'|[^\)]*\s*)\s*\)/ig; + var result, url, source; + + while (result = urlRegEx.exec(source)) { + url = result[3] || result[2] || result[5] || result[6] || result[4]; + var newUrl; + newUrl = convertURIBase(url, fromBase, toBase); + var quoteLen = result[5] || result[6] ? 1 : 0; + source = source.substr(0, urlRegEx.lastIndex - url.length - quoteLen - 1) + newUrl + source.substr(urlRegEx.lastIndex - quoteLen - 1); + urlRegEx.lastIndex = urlRegEx.lastIndex + (newUrl.length - url.length); + } + + return source; + }; + + normalizeCSS.convertURIBase = convertURIBase; + normalizeCSS.absoluteURI = absoluteURI; + normalizeCSS.relativeURI = relativeURI; + + return normalizeCSS; +}); +//>>excludeEnd('excludeRequireCss') diff --git a/src/check_config.php b/src/check_config.php index ab159aaf9b5..e78592db273 100644 --- a/src/check_config.php +++ b/src/check_config.php @@ -69,11 +69,18 @@ function libxml_display_error( $error ) function checkVersion( $dom ) { echo '
'; - $fileVersion = $dom->getElementsByTagName("pages")->item(0)->getAttribute('lib_version'); + $pages = $dom->getElementsByTagName("pages"); + if( 1 != $pages->length ) + { + echo 'Fatal error: Could not find <pages> element in config file!
'; + echo '(Note: this can also be caused by unbalanced elements, bad quotation marks, ...)'; + return; + } + $fileVersion = $pages->item(0)->getAttribute('lib_version'); echo "The config file uses a library version of '" . $fileVersion . "', current version is '" . LIBRARY_VERSION . "', so this is " . ($fileVersion==LIBRARY_VERSION?'':'NOT ') . "up to date."; if( $fileVersion != LIBRARY_VERSION ) - echo ' Pleiase run Configuration Upgrade when you are sure that the config file is valid XML.'; } diff --git a/src/cometvisu.appcache b/src/cometvisu.appcache index c0ecb253eef..d8359ef8b1c 100644 --- a/src/cometvisu.appcache +++ b/src/cometvisu.appcache @@ -1,5 +1,5 @@ CACHE MANIFEST -# Version SVN:20150128-0001 +# Version SVN:20150523-2349 CACHE: index.html @@ -15,6 +15,7 @@ icon/comet_webapp_icon_android_72.png icon/comet_webapp_icon_android_96.png icon/comet_webapp_icon_android_144.png icon/comet_webapp_icon_android_192.png +icon/iconconfig.js lib/templateengine.js # structure_pure will be inserted here during the release process !DON'T MODIFY! dependencies/scrollable.min.js @@ -32,8 +33,8 @@ dependencies/scrollable.js dependencies/jsfloorplan.js dependencies/Three.js dependencies/poly2tri.js -dependencies/yepnope.1.5.4.js lib/iconhandler.js +lib/icontools.js lib/compatibility.js lib/cometvisu-client.js lib/pagepartshandler.js diff --git a/src/config/.gitignore b/src/config/.gitignore new file mode 100644 index 00000000000..bd225e26160 --- /dev/null +++ b/src/config/.gitignore @@ -0,0 +1,5 @@ +# ignoring text configs +visu_* + +# but keep default configs +!visu_config.xml diff --git a/src/config/demo/visu_config_demo.xml b/src/config/demo/visu_config_demo.xml index 60fe3c7ce29..23d59695643 100644 --- a/src/config/demo/visu_config_demo.xml +++ b/src/config/demo/visu_config_demo.xml @@ -127,7 +127,7 @@ ]]> Check Config -
Version: SVN
+
Version: Git
]]>
@@ -144,6 +144,15 @@ + + + +
12/7/9
+
+
+
+ + - + @@ -248,6 +248,7 @@ + @@ -416,6 +417,7 @@ + @@ -944,6 +946,7 @@ + @@ -1074,12 +1077,22 @@ + + + + + + + + + + @@ -1094,7 +1107,7 @@ - + @@ -1305,4 +1318,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +