From 0792a3d8d7638ba1380b10fd0b4ce83221ae0c9b Mon Sep 17 00:00:00 2001 From: Rad Suchecki Date: Thu, 17 Jan 2019 11:55:33 +1030 Subject: [PATCH 01/16] Update README.md Preventing infinite wait when started docker container fails before coming on-line --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index deac76abf..28d9306bc 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,11 @@ For a quick start without installing any of the dependencies you will need docke ``` mkdir -p ~/mongodata \ && docker run --name mongo --detach --volume ~/mongodata:/data/db --net=host mongo \ - && until $(curl --silent --fail --output /dev/null localhost:27017); do printf '.'; sleep 1; done \ + && until $(curl --silent --output /dev/null localhost:27017 || \ + [ $(docker inspect -f '{{.State.Running}}' mongo) = "false" ]); do printf '.'; sleep 1; done \ && docker run --name pretzel --detach --net=host plantinformaticscollaboration/pretzel:stable \ - && until $(curl --silent --fail --output /dev/null localhost:3000); do printf '.'; sleep 1; done \ + && until $(curl --silent --output /dev/null localhost:3000 || \ + [ $(docker inspect -f '{{.State.Running}}' pretzel) = "false" ] ); do printf '.'; sleep 1; done \ && docker logs pretzel ``` From af7ae87c2e02281b5f1995b381854bff6be6d8f6 Mon Sep 17 00:00:00 2001 From: Rad Suchecki Date: Thu, 27 Feb 2020 14:19:41 +1030 Subject: [PATCH 02/16] applied liceseKey to upload Handsontable --- frontend/app/components/panel/upload/data-csv.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/panel/upload/data-csv.js b/frontend/app/components/panel/upload/data-csv.js index fa54dd229..02a979b5d 100644 --- a/frontend/app/components/panel/upload/data-csv.js +++ b/frontend/app/components/panel/upload/data-csv.js @@ -57,7 +57,13 @@ export default UploadBase.extend({ }, afterRemoveRow: function() { that.checkData(); - } + }, + /* + * The following line enables the app to work with versions after 6.2.2, + * refn : https://handsontable.com/blog/articles/2019/3/handsontable-drops-open-source-for-a-non-commercial-license + * This app is used for academic research. + */ + licenseKey: 'non-commercial-and-evaluation' }); that.set('table', table); $('.nav-tabs a[href="#left-panel-upload"]').on('shown.bs.tab', function() { @@ -175,7 +181,7 @@ export default UploadBase.extend({ return new Ember.RSVP.Promise(function(resolve, reject) { var selectedMap = that.get('selectDataset'); // If a selected dataset, can simply return it - // If no selectedMap, treat as default, 'new' + // If no selectedMap, treat as default, 'new' if (selectedMap && selectedMap !== 'new') { resolve(selectedMap); } else { From 9e9ccdf02465c5b9aaf816025793451554c70a3b Mon Sep 17 00:00:00 2001 From: Don Date: Sun, 1 Mar 2020 22:32:31 +1100 Subject: [PATCH 03/16] preserve .viewed and .extended by moving them from block to model and axis1d component respectively i.e. use model:mapsToView as the CP dependency for the list of viewed blocks. axis-2d.js : drop axis(), axis1d(), blocks(), dataBlocksS(). dataBlocks() : store width in this, instead of axis.extended. access axis1d instead of axis for .extended. axis-tracks.js : access .drawMap.oa from axis1d instead of axis. showTrackBlocks() : don't call layoutAndDrawTracks() if ! isViewed. draw-map.js : use queryParamsService.urlOptions. drop .axes2d. add a removeBlock() to actions. split out urlOptions to form urlOptions(), urlOptionsEffect(), optionsToDom() in added query-params.js. brushHelper() and zoom() : use ensureYscaleDomain(). featureY_(): use lookupFeature() if feature not yet in z. axes-1d.js : use dLog. draw/axis-1d.js : check if .isDestroyed before setting axisS.axis1d. drop axisObj() (it is provided just for transitional compatibility by hbs). use passed-in .extended, and change previous CP extended to extendedEffect. willDestroyElement() : call axis1dRemove(). stacks-view.js : axesBlocks : add dependencies blockValues.[], viewed.[]; axesP : add dependency viewed.[] paths-table.js : destroyHoTable() : check if table is defined. controllers/mapview.js : addMap() : use block.setViewed in place of viewed-blocks. updateRoute() : queryParams is now handled by query-params. removeBlock() : handle either blockId (string) or block. use block.unViewChildBlocks() and block.setViewed in place of mixins/viewed-blocks models/block.js : change isViewed to .computed based on mapsToView; add viewedChildBlocks(), unViewChildBlocks(). routes/mapview.js : model() : use .assign to merge URL params into queryParamsService.params so fields of params can be used in dependencies. add setViewedOnly to result. use blockService.setViewed in place of setViewedOnly(). data/block.js : drop injectParsedOptions, use service query-params instead, parsedOptions changes from a value to an alias. getIsViewed() : use params.mapsToView in place of block.isViewed add setViewed(). replace viewed() : use mapsToView and blocksById in place of filtering blockValues by .isViewed. add viewedById(), viewedAxisEffect(). viewedIds() : use mapsToView and blocksById in place of viewed. viewedBlocksByReferenceAndScope(): add dependency blockValues.[] draw-map.hbs : use queryParamsService.urlOptionsEffect; move use of axis-2d to axes-1d.hbs. axis-1d.hbs : use extendedEffect. mapview.hbs : simplify and unify action names : replace mapsToViewDelete=removeMap with removeBlock. utils/draw/axis.js : add ensureYscaleDomain(). feature-lookup.js : add lookupFeature(), using storeFeature2() factored out of storeFeature(). stacks.js : add Stacked.axis1dRemove(). frontend/app/ : dae8ac1 12277 Feb 28 21:57 components/axis-2d.js e8de044 26047 Mar 1 21:45 components/axis-tracks.js 7ad8df1 239208 Mar 1 18:59 components/draw-map.js d4a3613 847 Feb 28 21:57 components/draw/axes-1d.js 36daa57 28649 Feb 29 17:02 components/draw/axis-1d.js 3a6ecb7 3428 Feb 28 21:57 components/draw/stacks-view.js 9689b7f 27466 Feb 27 23:22 components/panel/paths-table.js c1caffb 11282 Mar 1 17:56 controllers/mapview.js 648afd7 15181 Feb 28 08:35 models/block.js 9e80b17 5561 Mar 1 17:56 routes/mapview.js e989266 38667 Mar 1 18:32 services/data/block.js 47e77a6 2222 Mar 1 18:15 services/query-params.js f3419c7 1938 Feb 28 21:57 templates/components/draw-map.hbs ca2ecc1 615 Feb 28 21:57 templates/components/draw/axes-1d.hbs 6cdfee9 665 Feb 29 17:02 templates/components/draw/axis-1d.hbs 12c3066 5344 Feb 27 23:55 templates/mapview.hbs 9d9d59f 8524 Mar 1 21:29 utils/draw/axis.js 7cf4136 7254 Feb 27 23:12 utils/feature-lookup.js 127c736 73151 Feb 27 23:43 utils/stacks.js --- frontend/app/components/axis-2d.js | 50 ++----- frontend/app/components/axis-tracks.js | 17 ++- frontend/app/components/draw-map.js | 76 ++++------- frontend/app/components/draw/axes-1d.js | 6 +- frontend/app/components/draw/axis-1d.js | 32 +++-- frontend/app/components/draw/stacks-view.js | 18 ++- frontend/app/components/panel/paths-table.js | 6 +- frontend/app/controllers/mapview.js | 46 ++++--- frontend/app/models/block.js | 27 +++- frontend/app/routes/mapview.js | 24 +++- frontend/app/services/data/block.js | 125 +++++++++++++----- frontend/app/services/query-params.js | 64 +++++++++ .../app/templates/components/draw-map.hbs | 8 +- .../app/templates/components/draw/axes-1d.hbs | 7 +- .../app/templates/components/draw/axis-1d.hbs | 1 + frontend/app/templates/mapview.hbs | 2 +- frontend/app/utils/draw/axis.js | 27 +++- frontend/app/utils/feature-lookup.js | 25 +++- frontend/app/utils/stacks.js | 9 +- 19 files changed, 381 insertions(+), 189 deletions(-) create mode 100644 frontend/app/services/query-params.js diff --git a/frontend/app/components/axis-2d.js b/frontend/app/components/axis-2d.js index 6e71d25b4..42a6c913a 100644 --- a/frontend/app/components/axis-2d.js +++ b/frontend/app/components/axis-2d.js @@ -13,9 +13,12 @@ const axisTransitionTime = 750; export default Ember.Component.extend(Ember.Evented, AxisEvents, { blockService: service('data/block'), + queryParams: service('query-params'), needs: ['component:tracks'], + urlOptions : Ember.computed.alias('queryParamsService.urlOptions'), + subComponents : undefined, targetEltId : Ember.computed('axisID', function() { @@ -24,42 +27,14 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { return id; }), - axis : Ember.computed('axisID', function() { - let axisID = this.get('axisID'); - let stacks = this.get('drawMap.oa.stacks'); - let axis = stacks.axesP[axisID]; - return axis; - }), - axis1d : Ember.computed('axis', 'drawMap.oa.stacks.axesPCount', function() { - let axis = this.get('axis'), - axesPCount = this.get('drawMap.oa.stacks.axesPCount'), - axis1d = axis.axis1d; - console.log('axis1d', axis1d, axesPCount); - return axis1d; - }), - /** @return the blocks of this axis. - * Result type is [] of (stack.js) Block. - * For the Ember data Object blocks, @see dataBlocks(). + /** Earlier versions had CP functions axis(), blocks(), dataBlocksS() based on + * stacks.js data structure for axes and blocks; these were dropped after + * version 1d6437a. */ - blocks : Ember.computed('axis', function() { - let axis = this.get('axis'); - return axis && axis.blocks; - }), - /** @return just the ("child") data blocks, skipping the ("parent") reference - * block which is block[0]. - * Result type is [] of (stack.js) Block. - */ - dataBlocksS : Ember.computed('blocks.[]', function () { - let blocks = this.get('blocks'), - /** skip reference block, which has no features. */ - dataBlocks = blocks.filter(function (bS) { - let b = bS.block; return b && bS.isData(); }); - console.log('dataBlocks', blocks, dataBlocks); - return dataBlocks; - }), - /** The above dataBlocksS() is based on stacks.js data structure for axes and blocks; - * this function instead is based on the Ember store blocks, via the ComputedProperty axesBlocks. - * This can replace dataBlocksS(), which may be updated after a delay. + + /** @return the list of data blocks of this axis. These are the Ember Data store + * blocks, collated based on the ComputedProperty axesBlocks. + * * @return [] if there are no blocks with data in the axis. */ dataBlocks : Ember.computed( @@ -337,9 +312,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { */ let axisID = me.get('axisID'); - let axis = me.get('axis'); - console.log('extended', axis.extended, width, axis); - axis.extended = width; + console.log('extended', this.get('axis1d.extended'), width); + this.set('width', width); currentSize = width; // dx ? let parentView = me.get('parentView'); diff --git a/frontend/app/components/axis-tracks.js b/frontend/app/components/axis-tracks.js index fef0d1fa4..efeb1ff3f 100644 --- a/frontend/app/components/axis-tracks.js +++ b/frontend/app/components/axis-tracks.js @@ -174,6 +174,8 @@ export default InAxis.extend({ didInsertElement() { this._super(...arguments); + + console.log("components/axis-tracks didInsertElement()"); let childWidths = this.get('childWidths'), axisID = this.get('axisID'), @@ -183,6 +185,11 @@ export default InAxis.extend({ childWidths.set(this.get('className'), [width, width]); }, + willDestroyElement() { + this._super(...arguments); + console.log("components/axis-tracks willDestroyElement()"); + }, + didRender() { console.log("components/axis-tracks didRender()"); }, @@ -337,7 +344,7 @@ export default InAxis.extend({ let axisID = this.get('axisID'), aS = selectAxis(axisID); let - oa = this.get('axis').drawMap.oa, // or pass in this.get('data'), + oa = this.get('axis1d.drawMap.oa'), axis = oa.axes[axisID]; /* if parent is un-viewed, this function may be called after axis is removed * from stacks. */ @@ -645,10 +652,13 @@ export default InAxis.extend({ let tracks = this.get('tracksTree'); let axis1d = this.get('axis1d'), zoomed = this.get('axis1d.zoomed'), + isViewed = axis1d.axis.get('isViewed'), extended = this.get('axis1d.extended'), featureLength = this.get('axis1d.featureLength'), yDomain = this.get('yDomain'); - console.log('showTrackBlocks', this, tracks, axis1d, yDomain, 'axis1d.zoomed', zoomed, extended, featureLength); + console.log('showTrackBlocks', this, tracks, axis1d, isViewed, yDomain, 'axis1d.zoomed', zoomed, extended, featureLength); + let featuresLength; + if (isViewed) { let blockIds = d3.keys(tracks.intervalTree); if (false) { let blockId = blockIds[0]; @@ -657,8 +667,9 @@ export default InAxis.extend({ } // intersect with axis zoom region; layer the overlapping tracks; draw tracks. this.layoutAndDrawTracks.apply(this, [undefined, tracks]); - let featuresLength = blockIds.map((blockId) => [blockId, tracks.intervalTree[blockId].intervals.length]); + featuresLength = blockIds.map((blockId) => [blockId, tracks.intervalTree[blockId].intervals.length]); console.log('showTrackBlocks() featuresLength', featuresLength); + } return featuresLength; }), diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index e7418dd02..26567df11 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -23,12 +23,12 @@ import { EventedListener } from '../utils/eventedListener'; import { chrData, cmNameAdd } from '../utils/utility-chromosome'; import { eltWidthResizable, eltResizeToAvailableWidth, noShiftKeyfilter, eltClassName, tabActive, inputRangeValue, expRange } from '../utils/domElements'; import { /*fromSelectionArray,*/ logSelectionLevel, logSelection, logSelectionNodes, selectImmediateChildNodes } from '../utils/log-selection'; -import { parseOptions } from '../utils/common/strings'; import { Viewport } from '../utils/draw/viewport'; import { AxisTitleLayout } from '../utils/draw/axisTitleLayout'; import { brushClip } from '../utils/draw/axisBrush'; import { Axes, maybeFlip, maybeFlipExtent, + ensureYscaleDomain, /*yAxisTextScale,*/ yAxisTicksScale, yAxisBtnScale, yAxisTitleTransform, eltId, axisEltId, eltIdAll /*, axisTitleColour*/ } from '../utils/draw/axis'; import { stacksAxesDomVerify } from '../utils/draw/stacksAxes'; import { Block, Stacked, Stack, stacks, xScaleExtend, axisRedrawText, axisId2Name, setCount } from '../utils/stacks'; @@ -58,7 +58,7 @@ import { collateStacks, countPaths, /*countPathsWithData,*/ * pathsAliasesResult.length) for all block-adj in flows.blockAdjs */ function countPathsWithData() { } -import { storeFeature } from '../utils/feature-lookup'; +import { storeFeature, lookupFeature } from '../utils/feature-lookup'; /*----------------------------------------------------------------------------*/ @@ -101,7 +101,10 @@ export default Ember.Component.extend(Ember.Evented, { blockService: service('data/block'), flowsService: service('data/flows-collate'), pathsP : service('data/paths-progressive'), + queryParamsService: service('query-params'), + /*--------------------------------------------------------------------------*/ + urlOptions : Ember.computed.alias('queryParamsService.urlOptions'), /*------------------------------------------------------------------------*/ //- graphData: Ember.inject.service('graph-data'), @@ -263,11 +266,8 @@ export default Ember.Component.extend(Ember.Evented, { scroller: Ember.inject.service(), - /** later axes can be all displayed axes, but in this first stage: just add them when they are extended */ - axes2d : [], - splitAxes: Ember.computed.filterBy('axes2d', 'extended', true), - - axisData : [{feature: "A1", position: 11}, {feature: "A2", position: 12}], + axes1d : Ember.computed( function () { return stacks.axes1d; }), + splitAxes: Ember.computed.filterBy('axes1d', 'extended', true), /*------------------------------------------------------------------------*/ @@ -319,33 +319,28 @@ export default Ember.Component.extend(Ember.Evented, { }, addMap : function(mapName) { - console.log("controller/draw-map", "addMap", mapName); + dLog("controller/draw-map", "addMap", mapName); this.sendAction('addMap', mapName); }, - mapsToViewDelete : function(mapName) { - console.log("controller/draw-map", "mapsToViewDelete", mapName); - this.sendAction('mapsToViewDelete', mapName); + + removeBlock(block) { + dLog('removeBlock', block.id); + this.sendAction('removeBlock', block); }, + enableAxis2D: function(axisID, enabled) { - let axes2d = this.get('axes2d'); - let axis = axes2d.findBy('axisID', axisID); + let axes1d = this.get('axes1d'); + let axis = axes1d[axisID]; if (axis === undefined) { - /* push will trigger : arrayContentDidChange() - * ... enumerableContentDidChange() ... didRender() (in axis-1d), so - * make give .extended its value before push. - */ - axis = Ember.Object.create({ axisID : axisID, 'extended' : enabled }); - axes2d.pushObject(axis); - console.log("create", axisID, axis, "in", axes2d); + dLog('enableAxis2D()', enabled, "no", axisID, "in", axes1d); } else Ember.run.later( () => axis.set('extended', enabled)); // was axis2DEnabled console.log("enableAxis2D in components/draw-map", axisID, enabled, axis); console.log("splitAxes", this.get('splitAxes')); - console.log("axes2d", this.get('axes2d')); }, axisWidthResize : function(axisID, width, dx) { @@ -1950,32 +1945,7 @@ export default Ember.Component.extend(Ember.Evented, { else svgContainer = oa.svgContainer; - let options_param = this.get('params.options'), options; - if (options_param && ! this.get('urlOptions') - && (options = parseOptions(options_param))) - { - /* splitAxes1 is now enabled by default. */ - if (! options.splitAxes1) - options.splitAxes1 = true; - this.set('urlOptions', options); - this.get('blockService').injectParsedOptions(options); - // alpha enables new features which are not yet robust. - options.splitAxes |= options.alpha; - /** In addition to the options which are added as body classes in the - * following statement, the other supported options are : - * splitAxes (enables buttons for extended axis and dot-plot in configureAxisTitleMenu()) - */ - d3.select('body') - // alpha enables alpha features e.g. extended/split-axes, dot plot, - .classed("alpha", options.alpha) - // chartOptions enables (left panel : view) "Chart Options" - .classed("chartOptions", options.chartOptions) - .classed("gotoFeature", options.gotoFeature) - .classed("devel", options.devel) // enables some trace areas - .classed("axis2dResizer", options.axis2dResizer) - .classed('allInitially', options.allInitially) - ; - } + let options = this.get('urlOptions'); function setCssVariable(name, value) { @@ -3687,7 +3657,7 @@ export default Ember.Component.extend(Ember.Evented, { let parentName = Block.axisName_parent(axisID), ysa = oa.ys[parentName], /** if d is object ID instead of name then featureIndex[] is used */ - feature = oa.z[axisID][d], // || oa.featureIndex[d], + feature = oa.z[axisID][d] || lookupFeature(oa, flowsService, oa.z, axisID, d), // || oa.featureIndex[d], aky = ysa(feature.location), /** As noted in header comment, path Y value requires adding axisY = ... yOffset(). * parentName not essential here because Block yOffset() follows .parent reference. */ @@ -3704,7 +3674,6 @@ export default Ember.Component.extend(Ember.Evented, { } - //- axis-brush-zoom /** Map brushedRegions into an array parallel to selectedAxes[]. */ @@ -4096,6 +4065,8 @@ export default Ember.Component.extend(Ember.Evented, { axisBrush = me.get('pathsP').ensureAxisBrush(block); console.log('axis', axis, axis.block, block, 'axisBrush', axisBrush); } + let yp = oa.y[brushedAxisID]; + ensureYscaleDomain(yp, oa.axes[brushedAxisID]); let brushedDomain = brushRange ? axisRange2Domain(brushedAxisID, brushRange) : undefined; axisBrush.set('brushedDomain', brushedDomain); @@ -4346,9 +4317,11 @@ export default Ember.Component.extend(Ember.Evented, { brushExtents[i] = brushedRegions[p]; } let yp = y[p], + ypDomain = yp.domain(), axis = oa.axes[p], domain, brushedDomain; + ensureYscaleDomain(yp, axis); if (brushExtents) { brushedDomain = brushExtents[i].map(function(ypx) { return yp.invert(ypx /* *axis.portion*/); }); // brushedDomain = [yp.invert(brushExtents[i][0]), yp.invert(brushExtents[i][1])]; @@ -5550,11 +5523,12 @@ export default Ember.Component.extend(Ember.Evented, { let stackID = Stack.removeStacked(axisName); deleteAxisfromAxisIDs(axisName); let sBlock = oa.stacks.blocks[axisName]; + let block = sBlock.block; console.log('sBlock.axis', sBlock.axis); sBlock.setAxis(undefined); removeBrushExtent(axisName); removeAxisMaybeStack(axisName, stackID, stack); - me.send('mapsToViewDelete', axisName); + me.send('removeBlock', axisName); // filter axisName out of selectedFeatures and selectedAxes selectedFeatures_removeAxis(axisName); sendUpdatedSelectedFeatures(); @@ -5648,7 +5622,7 @@ export default Ember.Component.extend(Ember.Evented, { .on('click', function (buttonElt /*, i, g*/) { console.log("delete", block.axisName, this); // this will do : block.block.setViewed(false); - me.send('mapsToViewDelete', block.axisName); + me.send('removeBlock', block.block); }); let visibleButtonS = d3.select("button.VisibleAxis"); diff --git a/frontend/app/components/draw/axes-1d.js b/frontend/app/components/draw/axes-1d.js index e0ce1b8b7..f50cc624b 100644 --- a/frontend/app/components/draw/axes-1d.js +++ b/frontend/app/components/draw/axes-1d.js @@ -4,6 +4,8 @@ import AxisEvents from '../../utils/draw/axis-events'; /* global d3 */ +const dLog = console.debug; + /** AxisEvents is commented out here - not currently required because axis-1d.js * registers for events using AxisEvents, filtering by matching axisID. * Planning to use AxisEvents here and propagate to just those matching axes. @@ -14,12 +16,12 @@ export default Ember.Component.extend(Ember.Evented, /*AxisEvents,*/ { * (todo) propagates it to its children */ axisStackChanged : function() { - console.log("axisStackChanged in components/axes-1d"); + dLog("axisStackChanged in components/axes-1d"); }, /** @param [axisID, t] */ zoomedAxis : function(axisID_t) { - console.log("zoomedAxis in components/axes-1d", axisID_t); + dLog("zoomedAxis in components/axes-1d", axisID_t); } /*--------------------------------------------------------------------------*/ diff --git a/frontend/app/components/draw/axis-1d.js b/frontend/app/components/draw/axis-1d.js index 02a6b8c60..e092ea9a4 100644 --- a/frontend/app/components/draw/axis-1d.js +++ b/frontend/app/components/draw/axis-1d.js @@ -265,11 +265,18 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, { init() { this._super(...arguments); + let axisName = this.get('axis.id'); /* axisS may not exist yet, so give Stacked a reference to this. */ Stacked.axis1dAdd(axisName, this); let axisS = this.get('axisS'); - if (! axisS || (axisS.axis1d && ! axisS.axis1d.isDestroyed)) + if (! axisS) { + dLog('axis-1d:init', this, axisName, this.get('axis')); + } + else if (axisS.axis1d === this) { + // no change + } + else if (axisS.axis1d && ! axisS.axis1d.isDestroyed) { dLog('axis-1d:init', this, axisName, this.get('axis'), axisS, axisS && axisS.axis1d); } @@ -691,23 +698,11 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, { return count; }), - axisObj : Ember.computed('axes2d.[]', function () { - let axes2d = this.get('axes2d'), - axisID = this.get('axis.id'), - axisObj = axes2d.findBy('axisID', axisID); - dLog('axes2d', axes2d, axisID, 'axisObj', axisObj, axisObj && axisObj.extended); - return axisObj; - }), - extended : Ember.computed('axisObj', 'axisObj.extended', function () { - let axisObj = this.get('axisObj'), - /** axes are (currently) added to axes2d when they are first extended, so if - * axis.id is not in the list then it is not extended. (Will likely replace - * axes2d with a list of sub-components, like axis-2d : subComponents but - * with axisID as source array) - */ - extended = (axisObj === undefined) ? false : this.get('axisObj.extended'), + extendedEffect : Ember.computed('extended', function () { + let + extended = this.get('extended'), axisID = this.get('axis.id'); - dLog('extended', extended, axisID, this.get('axisObj')); + dLog('extended', extended, axisID); if (extended) this.removeTicks(); else @@ -737,6 +732,9 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, { if (axisS.axis1d === this) delete axisS.axis1d; } + let axisName = this.get('axis.id'); + Stacked.axis1dRemove(axisName, this); + this._super(...arguments); }, removeTicks() { diff --git a/frontend/app/components/draw/stacks-view.js b/frontend/app/components/draw/stacks-view.js index 06b046ecd..da7151807 100644 --- a/frontend/app/components/draw/stacks-view.js +++ b/frontend/app/components/draw/stacks-view.js @@ -13,7 +13,18 @@ export default Ember.Component.extend({ /** Extract from viewedBlocksByReferenceAndScope(), the viewed blocks, mapped by the id of their reference block. */ - axesBlocks : Ember.computed('block.viewedBlocksByReferenceAndScope.@each', function () { + axesBlocks : Ember.computed( + /* viewedBlocksByReferenceAndScope is a Map, and there is not currently a way to + * depend on Map.@each, so depend on blockValues.[], which + * viewedBlocksByReferenceAndScope and blocksByReferenceAndScope depend on, + * and viewed.[] which viewedBlocksByReferenceAndScope depends on. + * (same applies in @see viewedBlocksByReferenceAndScope() + * It is possible that this could be called after a change in viewed, but + * before viewedBlocksByReferenceAndScope is updated; if this is a problem + * an update counter could be used as a dependency. + */ + 'block.viewedBlocksByReferenceAndScope.@each', 'blockValues.[]', 'viewed.[]', + function () { let mapByDataset = this.get('block.viewedBlocksByReferenceAndScope'); let mapByReferenceBlock = {}; if (mapByDataset) @@ -28,7 +39,10 @@ export default Ember.Component.extend({ return mapByReferenceBlock; }), - axesP : Ember.computed('axesBlocks.@each', function () { + /** Map the keys (blockId) of axesBlocks to the primary or reference block of each axis. + * @return array of blocks (store Object) + */ + axesP : Ember.computed('axesBlocks.@each', 'viewed.[]', function () { let axesBlocks = this.get('axesBlocks'), axisIDs = Object.keys(axesBlocks), axesP = axisIDs.map((axisID) => axesBlocks[axisID][0]); diff --git a/frontend/app/components/panel/paths-table.js b/frontend/app/components/panel/paths-table.js index 167206fb0..b3337f50c 100644 --- a/frontend/app/components/panel/paths-table.js +++ b/frontend/app/components/panel/paths-table.js @@ -160,8 +160,10 @@ export default Ember.Component.extend({ destroyHoTable() { let table = this.get('table'); dLog('destroyHoTable', table); - table.destroy(); - this.set('table', null); + if (table) { + table.destroy(); + this.set('table', null); + } }, diff --git a/frontend/app/controllers/mapview.js b/frontend/app/controllers/mapview.js index a4334104a..270023ade 100644 --- a/frontend/app/controllers/mapview.js +++ b/frontend/app/controllers/mapview.js @@ -4,8 +4,6 @@ import DS from 'ember-data'; const { computed : { readOnly } } = Ember; const { inject: { service } } = Ember; -import ViewedBlocks from '../mixins/viewed-blocks'; - /* global d3 */ const dLog = console.debug; @@ -15,7 +13,7 @@ dLog("controllers/mapview.js"); let trace_dataflow = 0; let trace_select = 0; -export default Ember.Controller.extend(Ember.Evented, ViewedBlocks, { +export default Ember.Controller.extend(Ember.Evented, { dataset: service('data/dataset'), block: service('data/block'), @@ -81,33 +79,45 @@ export default Ember.Controller.extend(Ember.Evented, ViewedBlocks, { */ addMap : function(mapName) { let block = this.get('blockFromId')(mapName), + blockId = mapName, + setViewed = this.get('block.setViewed'), referenceBlock = block.get('referenceBlock'); if (referenceBlock) - referenceBlock.get('setViewed').apply(this, [referenceBlock.get('id'), true]); - this.get('setViewed').apply(this, [mapName, true]); + setViewed(referenceBlock.get('id'), true); + setViewed(blockId, true); }, updateRoute() { let block_viewedIds = this.get('block.viewedIds'); dLog("controller/mapview", "updateRoute", this.target.currentURL, block_viewedIds); - let queryParams = - {'mapsToView' : block_viewedIds, - highlightFeature : this.get('model.params.highlightFeature') - }; + let queryParams = this.get('model.params'); let me = this; Ember.run.later( function () { me.transitionToRoute({'queryParams': queryParams }); }); }, + /** Un-view a block. + * @param block store object; this is only on difference from action removeMap(), + * which takes a blockId + */ removeBlock: function(block) { - let block_id = block.get('id'); - this.send('removeMap', block_id); + if (typeof block === "string") + this.send('removeMap', block); + else { + block.unViewChildBlocks(); + block.set('isViewed', false); + } }, /** Change the state of the named block to not-viewed. + * Equivalent to action removeBlock(), which takes a block record instead of a blockId. + * @param mapName blockId */ removeMap : function(mapName) { - /* delay to avoid nextSibling of null in insertAfter() */ - Ember.run.later(() => this.get('setViewed').apply(this, [mapName, false])); + let blockId = mapName; + let block = this.blockFromId(blockId); + block.unViewChildBlocks(); + + this.get('block').setViewed(mapName, false); }, onDelete : function (modelName, id) { @@ -131,7 +141,7 @@ export default Ember.Controller.extend(Ember.Evented, ViewedBlocks, { /** also load parent block */ loadBlock : function loadBlock(block) { dLog('loadBlock', block); - // also done in useTask() : (mixins/viewed-blocks)setViewed() : (data/block.js)setViewedTask() + // previously done in useTask() : (mixins/viewed-blocks)setViewed() : (data/block.js)setViewedTask() block.set('isViewed', true); let referenceBlock = block.get('referenceBlock'); if (referenceBlock) @@ -236,13 +246,11 @@ export default Ember.Controller.extend(Ember.Evented, ViewedBlocks, { }.observes('target.currentURL'), - blockTasks : readOnly('model.viewedBlocks.blockTasks'), /** all available */ blockValues : readOnly('block.blockValues'), - /** currently viewed */ - blockIds : readOnly('model.viewedBlocks.blockIds'), - + /** same as services/data/block @see peekBlock() + */ blockFromId : function(blockId) { let store = this.get('store'), block = store.peekRecord('block', blockId); @@ -263,7 +271,7 @@ export default Ember.Controller.extend(Ember.Evented, ViewedBlocks, { /** Update queryParams and URL. */ queryParamsValue : Ember.computed( - 'block.viewedIds', 'block.viewedIds.[]', 'block.viewedIds.length', + 'model.params.mapsToView.[]', function() { dLog('queryParamsValue'); this.send('updateRoute'); diff --git a/frontend/app/models/block.js b/frontend/app/models/block.js index 8be6e96b3..3b794b66a 100644 --- a/frontend/app/models/block.js +++ b/frontend/app/models/block.js @@ -3,6 +3,7 @@ import DS from 'ember-data'; import attr from 'ember-data/attr'; // import { PartialModel, partial } from 'ember-data-partial-model/utils/model'; const { inject: { service } } = Ember; +// import { computed, set } from '@ember/object'; import { A } from '@ember/array'; import { and } from '@ember/object/computed'; @@ -44,7 +45,19 @@ export default DS.Model.extend({ * set by adding the block to the graph (entry-block: get()), * and cleared by removing the block from the display. */ - isViewed: false, + isViewed: Ember.computed('blockService.params.mapsToView.[]', { + get () { + // alternate dependency : 'blockService.viewed.[]' + let isViewed = this.get('blockService').getIsViewed(this.get('id')); + return isViewed; + }, + set(key, value) { + dLog('isViewed', key, value); + this.get('blockService').setViewed(this.get('id'), value); + + return value; + } + }), /** undefined if ! isViewed, otherwise handle of Block in Stacked axis which displays this block. * This attribute can split out into a mixin, in that case could merge with stacks.js : Block. */ @@ -335,6 +348,18 @@ export default DS.Model.extend({ childBlocks = blocksByReference && blocksByReference.get(this); return childBlocks || []; }), + viewedChildBlocks : Ember.computed('childBlocks.@each.isViewed', function () { + let childBlocks = this.get('childBlocks'), + viewedChildBlocks = childBlocks.filterBy('isViewed'); + dLog('viewedChildBlocks', viewedChildBlocks, childBlocks); + return viewedChildBlocks; + }), + unViewChildBlocks() { + let viewedChildBlocks = this.get('viewedChildBlocks'); + if (viewedChildBlocks.length) + this.get('blockService').setViewed(viewedChildBlocks, false); + }, + /*--------------------------------------------------------------------------*/ diff --git a/frontend/app/routes/mapview.js b/frontend/app/routes/mapview.js index 1c8b09158..8ab1088ec 100644 --- a/frontend/app/routes/mapview.js +++ b/frontend/app/routes/mapview.js @@ -14,6 +14,7 @@ const dLog = console.debug; let config = { dataset: service('data/dataset'), block: service('data/block'), + queryParamsService: service('query-params'), titleToken: 'MapView', queryParams: { @@ -55,16 +56,23 @@ let config = { /** Ember-concurrency tasks are returned in the model : * availableMapsTask : task -> [ id , ... ] - * blockTasks : { id : task, ... } + * viewedBlocks : allinitially ? (blockTasks : { id : task, ... }) : single task for getBlocksSummary(). */ - model(params) { + model(paramsIn) { // Get all available maps. let result; let me = this; + let blockService = this.get('block'); + /** blockService supports in computing the model. */ + let params = this.get('queryParamsService').get('params'); + dLog('paramsIn', paramsIn, params, paramsIn.mapsToView, params.mapsToView); + Object.assign(params, paramsIn); + dLog('params', params, params.mapsToView); + if (params.options) params.parsedOptions = parseOptions(params.options); @@ -72,9 +80,8 @@ let config = { let taskGetList = datasetService.get('taskGetList'); // availableMaps let datasetsTask = taskGetList.perform(); // renamed from 'maps' - this.controllerFor(this.fullRouteName).setViewedOnly(params.mapsToView, true); + // this.controllerFor(this.fullRouteName).setViewedOnly(params.mapsToView, true); - let blockService = this.get('block'); let blocksLimitsTask = this.get('blocksLimitsTask'); dLog('blocksLimitsTask', blocksLimitsTask); if (! blocksLimitsTask || ! blocksLimitsTask.get('isRunning')) { @@ -90,7 +97,8 @@ let config = { { params : params, availableMapsTask : datasetsTask, // task result is -> [ id , ... ] - viewedBlocks : viewedBlocksTasks + viewedBlocks : viewedBlocksTasks, + viewedById : blockService.get('viewedById') }); /* When the datasets result (actually the blocks) is received, use that @@ -110,8 +118,10 @@ let config = { result.push(referenceBlock); return result;}, []), referenceBlockIds = referenceBlocks.map(function (block) { return block.get('id'); }); - dLog('referenceBlockIds', referenceBlockIds); - me.controllerFor(me.fullRouteName).setViewedOnly(referenceBlockIds, true); + if (referenceBlockIds.length) { + dLog('referenceBlockIds', referenceBlockIds); + blockService.setViewed(referenceBlockIds, true); + } /* currently getBlocksSummary() just gets the featureCount, which for a * reference block is 0, so this step could be skipped if ! allInitially, * but later the summary may contain other information */ diff --git a/frontend/app/services/data/block.js b/frontend/app/services/data/block.js index 5bae0f16b..3b3e2ded1 100644 --- a/frontend/app/services/data/block.js +++ b/frontend/app/services/data/block.js @@ -4,6 +4,8 @@ import { task } from 'ember-concurrency'; const { inject: { service } } = Ember; +import { keyBy } from 'lodash/collection'; + import { stacks } from '../../utils/stacks'; @@ -69,19 +71,21 @@ function filterMap(map, mapFilterFn) { * It is possible that later there will be multiple mapviews in one route, in * which case the isViewed state might be split out of this singleton service, * or this may become a per-mapview component. - * + * + * @param params model.params; the viewed CPs are based on .mapsToView from the URL. */ export default Service.extend(Ember.Evented, { auth: service('auth'), store: service(), pathsPro : service('data/paths-progressive'), flowsService: service('data/flows-collate'), + queryParams: service('query-params'), + + params : Ember.computed.alias('queryParams.params'), summaryTask : {}, - injectParsedOptions(parsedOptions) { - this.set('parsedOptions', parsedOptions); - }, + parsedOptions : Ember.computed.alias('queryParamsService.urlOptions'), /** Not required because findRecord() is used; * might later want this for other requests or calculation results, but can @@ -106,8 +110,12 @@ export default Service.extend(Ember.Evented, { /** Call getData() in a task - yield the block result. * Signal that receipt with receivedBlock(id, block). + * + * taskGet()->getData() is only used if allInitially (i.e. not progressive loading); + * so there is no current need for getBlocks(), taskGet() and getData(). */ taskGet: task(function * (id) { + debugger; // see header comment /** if not already loaded and viewed, then trigger receivedBlock */ let isViewed = this.get('getIsViewed').apply(this, [id]); let block = yield this.getData(id); @@ -120,6 +128,7 @@ export default Service.extend(Ember.Evented, { return block; }), getData: function (id) { + debugger; // see header comment in taskGet(); // console.log("block getData", id); let store = this.get('store'); let allInitially = this.get('parsedOptions.allInitially'); @@ -400,6 +409,7 @@ export default Service.extend(Ember.Evented, { /*--------------------------------------------------------------------------*/ /** @return the block record handle if the block is loaded into the store from the backend. + * @desc same as controllers/mapview @see blockFromId() */ peekBlock(blockId) { @@ -410,15 +420,44 @@ export default Service.extend(Ember.Evented, { /*--------------------------------------------------------------------------*/ - /** @return true if the block is loaded into the store from the backend, and has .isViewed==true. + /** @return true if the blockId is in the URL mapsToView */ getIsViewed(blockId) { - let store = this.get('store'), - block = store.peekRecord('block', blockId), - isViewed = block && block.get('isViewed'); + let + viewedIds = this.get('params.mapsToView'), + index = viewedIds.indexOf(blockId), + isViewed = (index >= 0); return isViewed; }, + /** Set the viewed state of the given blockId. + * @param blockId may be an array of blockIds, in which case the function is called for each element blockId + */ + setViewed(blockId, viewed) { + if (Ember.isArray(blockId)) + { + blockId.forEach((blockId) => this.setViewed(blockId, viewed)); + } + else { + if (typeof blockId !== "string") + blockId = blockId.get('id'); + let viewedIds = this.get('params.mapsToView'), + index = viewedIds.indexOf(blockId); + dLog('setViewed', blockId, viewed, viewedIds, index, this); + if (viewed === (index >= 0)) { + // no change + } + else if (viewed) { + viewedIds.pushObject(blockId); + } + else { + let removed = viewedIds.objectAt(index); + viewedIds.removeAt(index, 1); + dLog('setViewed removed', removed); + } + } + }, + /*--------------------------------------------------------------------------*/ @@ -437,6 +476,7 @@ export default Service.extend(Ember.Evented, { * @return define a task */ setViewedTask: task(function * (id, viewed, unviewChildren) { + debugger; // only used in viewed-blocks console.log("setViewedTask", id, viewed, unviewChildren); let getData = this.get('getData'); /* - if ! viewed then no need to getData(), just Peek and if not loaded then return. @@ -488,6 +528,9 @@ export default Service.extend(Ember.Evented, { /*--------------------------------------------------------------------------*/ + /** Only used if allInitially (i.e. not progressive loading) + * @see taskGet() + */ getBlocks(blockIds) { let taskGet = this.get('taskGet'); console.log("getBlocks", blockIds); @@ -593,36 +636,44 @@ export default Service.extend(Ember.Evented, { console.log('selected', records); return records; // .toArray() }), - viewed: Ember.computed( + viewed : Ember.computed( + 'blocksById', 'params.mapsToView.[]', 'blockValues.[]', - 'blockValues.@each.isViewed', - function() { - let records = this.get('store').peekAll('block') // this.get('blockValues') - .filterBy('isViewed', true); - if (trace_block) - console.log('viewed', records.toArray()); - // can separate this to an added CP viewedEffect - let axes = records.map((block) => this.blockAxis(block)); + function () { + let blocksById = this.get('blocksById'), + viewedIds = this.get('params.mapsToView'), + /** mapsToView[] is defined from URL, but blocksById[] is defined later from + * API response. So filter out blocks which are not yet loaded. + */ + viewed = viewedIds.map((blockId) => blocksById[blockId]) + .filter((block) => block); + return viewed; + }), + viewedById : Ember.computed('viewed.[]', function () { + let viewed = this.get('viewed'), + viewedById = keyBy(viewed, (b) => b.id); + return viewedById; + }), + /** Ensure that there is an axis for each viewed block. + */ + viewedAxisEffect : Ember.computed('viewed.[]', function () { + let viewed = this.get('viewed'), + axes = viewed.map((block) => this.blockAxis(block)); console.log('viewed axes', axes); - return records; // .toArray() - }), + return axes; + }), + viewedIds: Ember.computed( - 'blockValues.[]', - 'blockValues.@each.isViewed', - 'viewed.[]', + 'params.mapsToView.[]', + 'blocksById.[]', function() { - let ids = this.get('viewed'); + let + ids = this.get('params.mapsToView'), + blocksById = this.get('blocksById'); if (trace_block > 1) - ids.map(function (a) { console.log('viewedIds', a, a.get('id')); } ); - if (trace_block) - console.log('viewedIds', ids); - ids = ids.map(function (a) { return a.get('id'); } ); - if (trace_block) - console.log('viewedIds', ids); - + ids.map(function (id) { console.log('viewedIds', id, blocksById[id]); } ); return ids; - }) - , + }), viewedScopes: Ember.computed( 'viewed.[]', function() { @@ -768,7 +819,13 @@ export default Service.extend(Ember.Evented, { /** filter blocksByReferenceAndScope() for viewed blocks, * @return Map, which may be empty */ - viewedBlocksByReferenceAndScope : Ember.computed('blocksByReferenceAndScope', 'viewed.[]', function () { + viewedBlocksByReferenceAndScope : Ember.computed( + /* blocksByReferenceAndScope is a Map, and there is not currently a way to + * depend on Map.@each, so depend on blockValues.[] which + * blocksByReferenceAndScope depends on, and viewed.[]. + */ + 'blocksByReferenceAndScope', 'blockValues.[]', + 'viewed.[]', function () { const fnName = 'viewedBlocksByReferenceAndScope'; let viewed = this.get('viewed'); let map = this.get('blocksByReferenceAndScope'), @@ -1014,7 +1071,7 @@ export default Service.extend(Ember.Evented, { }, /** Collate the viewed blocks by their parent block id, or by their own block * id if they are not parented. - * @return Map : blockId -> [blockId] + * @return Map : blockId -> [block] * @description * Similar to @see axesBlocks(). */ diff --git a/frontend/app/services/query-params.js b/frontend/app/services/query-params.js new file mode 100644 index 000000000..9ad436065 --- /dev/null +++ b/frontend/app/services/query-params.js @@ -0,0 +1,64 @@ +import Ember from 'ember'; +import Service from '@ember/service'; + +// const { inject: { service } } = Ember; + +// import { queryParam } from 'ember-query-params-service'; + +import { parseOptions } from '../utils/common/strings'; + +/* global d3 */ + + + +export default Service.extend(Ember.Evented, { + // block: service('data/block'), + + params : Ember.Object.create({}), + + /*--------------------------------------------------------------------------*/ + + /** Parse params.options. + */ + urlOptions : Ember.computed(function () { + /** No dependency is given because params will change, but these options aren't + * changeable at runtime by user action (although such could be added later). + */ + let options_param = this.get('params.options'), options; + if (options_param && (options = parseOptions(options_param))) + { + /* splitAxes1 is now enabled by default. */ + if (! options.splitAxes1) + options.splitAxes1 = true; + // alpha enables new features which are not yet robust. + options.splitAxes |= options.alpha; + } + return options; + }), + urlOptionsEffect: Ember.computed('urlOptions', function () { + let options = this.get('urlOptions'); + if (options) + this.optionsToDom(options); + // enable to see the results of parseOptions() on screen. + // return JSON.stringify(options); + }), + optionsToDom(options) { + /** In addition to the options which are added as body classes in the + * following statement, the other supported options are : + * splitAxes (enables buttons for extended axis and dot-plot in configureAxisTitleMenu()) + */ + d3.select('body') + // alpha enables alpha features e.g. extended/split-axes, dot plot, + .classed("alpha", options.alpha) + // chartOptions enables (left panel : view) "Chart Options" + .classed("chartOptions", options.chartOptions) + .classed("gotoFeature", options.gotoFeature) + .classed("devel", options.devel) // enables some trace areas + .classed("axis2dResizer", options.axis2dResizer) + .classed('allInitially', options.allInitially) + ; + }, + + /*--------------------------------------------------------------------------*/ + +}); diff --git a/frontend/app/templates/components/draw-map.hbs b/frontend/app/templates/components/draw-map.hbs index 9c7166505..7e823e7e5 100644 --- a/frontend/app/templates/components/draw-map.hbs +++ b/frontend/app/templates/components/draw-map.hbs @@ -1,3 +1,5 @@ +{{queryParamsService.urlOptionsEffect}} +
@@ -17,12 +19,6 @@ featuresInBlocks=featuresInBlocks }} {{!-- This info will be thinned or removed as axes development progresses. --}} -
splitAxes : {{splitAxes}}
-
axes2d : {{axes2d.length}}
- {{#each axes2d as |axisS|}} -
axis : {{axisS}}, {{axisS.axisID}}, {{axisS.extended}}
- {{axis-2d drawMap=this data=oa axisID=axisS.axisID tableData=axisData urlOptions=urlOptions}} - {{/each}} {{!-- wait until targetId="toolTip" will resolve --}} {{#if toolTipCreated }} diff --git a/frontend/app/templates/components/draw/axes-1d.hbs b/frontend/app/templates/components/draw/axes-1d.hbs index 338f33f03..e67d68760 100644 --- a/frontend/app/templates/components/draw/axes-1d.hbs +++ b/frontend/app/templates/components/draw/axes-1d.hbs @@ -1,7 +1,12 @@
axesP : {{axesP.length}}
{{#each axesP as |axis axisIndex|}} -
axis : {{axisIndex}}, {{axis.extended}}
+ {{!-- axis is the primary / reference block of the axis --}} {{#draw/axis-1d drawMap=drawMap axis=axis axes2d=axes2d as |axis1d|}} +
axis : {{axis1d.axis.id}}, {{axis1d.extended}}
+ {{#if axis1d.extended}} + {{axis-2d drawMap=this data=drawMap.oa axis1d=axis1d axisID=axis1d.axis.id axisObj=(hash axisID=axis1d.axis.id extended=axis1d.extended) }} + {{/if}} + {{draw/axis-ticks-selected axis1d=axis1d axisId=axis.id drawMap=drawMap featuresInBlocks=featuresInBlocks diff --git a/frontend/app/templates/components/draw/axis-1d.hbs b/frontend/app/templates/components/draw/axis-1d.hbs index adbd73de5..da1bcce70 100644 --- a/frontend/app/templates/components/draw/axis-1d.hbs +++ b/frontend/app/templates/components/draw/axis-1d.hbs @@ -2,6 +2,7 @@ will cause displaying their value to produce an exception, refn https://github.com/emberjs/ember.js/issues/13948 --}} axis-1d: {{ extended }} {{ axis.id }}, {{!-- axis.view}} {{axis.view.axisName , --}} {{ domainChanged }} {{ scaleChanged }} , {{ position }}, {{ currentPosition }}, {{ lastDrawn }}, {{!-- currentPosition.yDomain --}} {{ zoomed }}, {{ dataBlocks.length }} {{ dataBlocks.0.features.length }} {{blockIndexes}} {{ featureLengthEffect }} +{{ extendedEffect }} {{ colourSlots }} {{ ensureAxis }} {{ log 'axis-1d rendered domainContents' position }} diff --git a/frontend/app/templates/mapview.hbs b/frontend/app/templates/mapview.hbs index f504acaee..b9ed2975a 100644 --- a/frontend/app/templates/mapview.hbs +++ b/frontend/app/templates/mapview.hbs @@ -56,7 +56,7 @@ params=model.params selectedBlock=selectedBlock addMap='addMap' - mapsToViewDelete='removeMap' + removeBlock=(action 'removeBlock') updatedSelectedFeatures='updateSelectedFeatures' featuresInBlocks=featuresInBlocks selectChromById='selectBlockById'}} diff --git a/frontend/app/utils/draw/axis.js b/frontend/app/utils/draw/axis.js index a62607353..a21d75c3d 100644 --- a/frontend/app/utils/draw/axis.js +++ b/frontend/app/utils/draw/axis.js @@ -49,6 +49,28 @@ function noDomain(domain) { /*----------------------------------------------------------------------------*/ +/** Check that y axis scale yp.domain() is initialised, and if not, + * define it from .featureLimits of first block of axis + * + * axis-1d : domainChanged()->updateScaleDomain() can handle this, but perhaps + * that needs an added dependency. + * + * @param axis e.g. oa.axes[brushedAxisID] +*/ +function ensureYscaleDomain(yp, axis) { + if (! yp.domain().length) { + let block0 = axis && axis.blocks.length && axis.blocks[0], + block0featureLimits = block0 && block0.block && block0.block.featureLimits; + if (block0featureLimits) { + // for GM, blocks[0] has .featureLimits and not .range + dLog('block0featureLimits', block0featureLimits, axis.axisName, block0.longName()); + yp.domain(block0featureLimits); + } + } +} + +/*----------------------------------------------------------------------------*/ + /** For within a g.axis-outer, counteract the effect of g.axis-outer scale() which * is based on axis.portion. * @@ -222,4 +244,7 @@ function axisTitleColour (d, i) { /*----------------------------------------------------------------------------*/ -export { Axes, maybeFlip, maybeFlipExtent, noDomain, yAxisTextScale, yAxisTicksScale, yAxisBtnScale, yAxisTitleTransform, eltId, axisEltId, eltIdAll, axisEltIdClipPath, highlightId, axisTitleColour } ; +export { + Axes, maybeFlip, maybeFlipExtent, noDomain, + ensureYscaleDomain, + yAxisTextScale, yAxisTicksScale, yAxisBtnScale, yAxisTitleTransform, eltId, axisEltId, eltIdAll, axisEltIdClipPath, highlightId, axisTitleColour } ; diff --git a/frontend/app/utils/feature-lookup.js b/frontend/app/utils/feature-lookup.js index 47dd935ae..a85e6f9f8 100644 --- a/frontend/app/utils/feature-lookup.js +++ b/frontend/app/utils/feature-lookup.js @@ -133,6 +133,11 @@ function storeFeature(oa, flowsService, feature, f, axisID) { dLog('storeFeature', axisID, oa.z[axisID]); } } + storeFeature2(oa, flowsService, feature, f, axisID); +} +/** Same params as storeFeature(). + */ +function storeFeature2(oa, flowsService, feature, f, axisID) { if (axisID && ! oa.z[axisID][feature]) { /* when called from paths-progressive, emulate the data structure created by * chrData() / receiveChr() @@ -167,9 +172,25 @@ function storeFeature(oa, flowsService, feature, f, axisID) { oa.featureIndex[f.id] = f; } +import { stacks, Stacked } from './stacks'; + +/** called when z[axisID] does not contain [d]. + * Lookup feature d, and add it to z[axisID]. + * @param featureName name of feature (ID) + */ +function lookupFeature(oa, flowsService, z, axisID, featureName) { + let + axis = Stacked.getAxis(axisID), + index = axis.blocks.findIndex((b) => b.axisName === axisID), + features = axis.blocks[index].block.get('features'), + feature = features.find((f) => f.get('name') === featureName); + storeFeature2(oa, flowsService, featureName, feature, axisID); + return feature; +} + + /*----------------------------------------------------------------------------*/ -import { stacks } from './stacks'; /** @param features contains the data attributes of the features. */ function ensureBlockFeatures(blockId, features) { @@ -189,4 +210,4 @@ function ensureBlockFeatures(blockId, features) { /*----------------------------------------------------------------------------*/ -export { featureChrs, name2Map, chrMap, objectSet, mapsOfFeature, storeFeature, ensureBlockFeatures }; +export { featureChrs, name2Map, chrMap, objectSet, mapsOfFeature, storeFeature, lookupFeature, ensureBlockFeatures }; diff --git a/frontend/app/utils/stacks.js b/frontend/app/utils/stacks.js index 6abe9519e..e49612c62 100644 --- a/frontend/app/utils/stacks.js +++ b/frontend/app/utils/stacks.js @@ -269,8 +269,7 @@ function Stacked(axisName, portion) { this.blocks = []; /* Pick up the reference to the corresponding axis-1d component, in the case * that it was created before this Stacked. */ - if (axes1d[axisName]) - this.axis1d = axes1d[axisName]; + this.getAxis1d(); /* axis objects persist through being dragged in and out of Stacks. */ axesP[axisName] = oa.axes[axisName] = this; @@ -302,6 +301,12 @@ Stacked.prototype.getAxis = function() Stacked.axis1dAdd = function (axisName, axis1dComponent) { axes1d[axisName] = axis1dComponent; }; +Stacked.axis1dRemove = function (axisName, axis1dComponent) { + if (axes1d[axisName] !== axis1dComponent) + Ember.assert('axis1dRemove', axes1d, axisName, axis1dComponent); + else + delete axes1d[axisName]; +}; Stacked.prototype.getAxis1d = function () { let axis1d = this.axis1d || (this.axis1d = axes1d[this.axisName]); if (axis1d && (axis1d.isDestroying || axis1d.isDestroying)) { From bda87bec8ab1377f5d14035de3c078c9f3fe27dc Mon Sep 17 00:00:00 2001 From: Don Date: Mon, 2 Mar 2020 15:07:29 +1100 Subject: [PATCH 04/16] reduce trace. allow Namespace to wrap in dataset right panel. for #172 : app.scss : .dataset-table : width inherit instead of 100%, add extra selectors (right-panel-dataset) for priority. manage-dataset.hbs : Namespace td : overflow-wrap : break-word frontend/app/ : afd2adb 239253 Mar 2 14:32 components/draw-map.js 14708e4 28720 Mar 2 14:32 components/draw/axis-1d.js bbe4703 19492 Mar 2 14:32 components/draw/block-adj.js 7b5015a 18115 Mar 2 14:32 models/block-adj.js b917528 15229 Mar 2 14:32 models/block.js fcbdcf0 38671 Mar 2 14:32 services/data/block.js 49afdfd 26015 Mar 2 14:32 services/data/paths-progressive.js 3c15813 31735 Mar 2 14:51 styles/app.scss d36b65e 1865 Mar 2 14:51 templates/components/panel/manage-dataset.hbs 5a0710b 11708 Mar 2 14:32 utils/domElements.js --- frontend/app/components/draw-map.js | 4 +++- frontend/app/components/draw/axis-1d.js | 9 ++++++--- frontend/app/components/draw/block-adj.js | 13 ++++++++----- frontend/app/models/block-adj.js | 3 ++- frontend/app/models/block.js | 6 ++++-- frontend/app/services/data/block.js | 2 +- frontend/app/services/data/paths-progressive.js | 9 +++++++-- frontend/app/styles/app.scss | 2 ++ .../templates/components/panel/manage-dataset.hbs | 2 +- frontend/app/utils/domElements.js | 3 ++- 10 files changed, 36 insertions(+), 17 deletions(-) diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index 26567df11..4ad8062ae 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -1918,7 +1918,8 @@ export default Ember.Component.extend(Ember.Evented, { let resizeThis = // this.resize.bind(oa); function(transition) { - console.log("resizeThis", transition); + if (trace_stack) + dLog("resizeThis", transition); Ember.run.debounce(oa, me.resize, [transition], 500); }; /** d3 dispatch.on() does not take arguments, and similarly for eltWidthResizable() param resized. */ @@ -6076,6 +6077,7 @@ export default Ember.Component.extend(Ember.Evented, { * 't[m] is not a function, at Object.applyStr (ember-utils.js:524)' */ resized : function(prevSize, currentSize) { + if (trace_gui) console.log("resized in components/draw-map", this, prevSize, currentSize); }, diff --git a/frontend/app/components/draw/axis-1d.js b/frontend/app/components/draw/axis-1d.js index e092ea9a4..156bbe1ad 100644 --- a/frontend/app/components/draw/axis-1d.js +++ b/frontend/app/components/draw/axis-1d.js @@ -112,7 +112,8 @@ FeatureTicks.prototype.showTickLocations = function (featuresOfBlockLookup, setu gS.exit().remove(); function storeBlockIndex (block, i) { blockIndex[block.getId()] = i; - dLog('blockIndex', block.getId(), i); + if (trace_stack) + dLog('blockIndex', block.getId(), i); }; let gA = gS.enter() .append('g') @@ -122,7 +123,8 @@ FeatureTicks.prototype.showTickLocations = function (featuresOfBlockLookup, setu /** data blocks of the axis, for calculating blockIndex i.e. colour. * colour assignment includes non-visible blocks . */ let blocksUnfiltered = extended ? [] : axis.dataBlocks(false); - dLog('blockIndex', axisName, axis, axis.blocks); + if (trace_stack) + dLog('blockIndex', axisName, axis, axis.blocks); blocksUnfiltered.forEach(storeBlockIndex); function featuresOfBlock (block) { @@ -297,7 +299,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, { /* useTransition could be passed down to showTickLocations() * (also could pass in duration or t from showResize()). */ - dLog("resized in components/axis-1d"); + if (trace_stack) + dLog("resized in components/axis-1d"); if (heightChanged) this.renderTicksDebounce(); }, diff --git a/frontend/app/components/draw/block-adj.js b/frontend/app/components/draw/block-adj.js index a3bdea7b9..0b111bd17 100644 --- a/frontend/app/components/draw/block-adj.js +++ b/frontend/app/components/draw/block-adj.js @@ -249,7 +249,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { .attr('class', datumIdent) .each(function (d, i, g) { dLog(this); me.connectFlowControl(d, d3.select(this)); } ), pM = pS.merge(pA); - dLog('drawGroupContainer', pS.nodes(), pS.node()); + if (trace_blockAdj) + dLog('drawGroupContainer', pS.nodes(), pS.node()); return pM; }, /** Render the which contains the @@ -399,8 +400,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { .selectAll("path." + className) // don't call pathU() if axes are gone. .filter(function (d) { return d.blocksHaveAxes(); }); - if (! pS.empty()) - dLog('updatePathsPosition before update pS', pS.nodes(), pS.node()); + if (! pS.empty() && trace_blockAdj) + dLog('updatePathsPosition before update pS', (trace_blockAdj > 1) ? pS.nodes() : pS.size(), pS.node()); /* now that paths are within , path position can be altered * during dragging by updating a skew transform of , instead of * repeatedly recalculating pathU. @@ -450,7 +451,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { this.get('blockAdj.axes1d.1.scaleChanged')], zoomCounter = this.get('blockAdj.zoomCounter'), heightChanged = this.get('heightChanged'); - dLog('updatePathsPositionDebounce', this.get('blockAdjId'), heightChanged, count, flips, zoomCounter, scaleChanges, + if (trace_blockAdj) + dLog('updatePathsPositionDebounce', this.get('blockAdjId'), heightChanged, count, flips, zoomCounter, scaleChanges, stacksWidthChanges, this.get('block.stacksCount')); this.updatePathsPosition(); @@ -475,7 +477,8 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { /* useTransition could be passed down to draw() * (also could pass in duration or t from showResize()). */ - dLog("resized in components/block-adj"); + if (trace_blockAdj > 1) + dLog("resized in components/block-adj"); /* In addition to the usual causes of repeated events, block-adj will * respond to events relating to 2 axes. */ if (widthChanged) diff --git a/frontend/app/models/block-adj.js b/frontend/app/models/block-adj.js index dad841098..27739cacd 100644 --- a/frontend/app/models/block-adj.js +++ b/frontend/app/models/block-adj.js @@ -63,7 +63,8 @@ export default DS.Model.extend(Ember.Evented, { /** also check referenceBlocks because clicking on axis selects the reference block */ let referenceBlocks = this.get('referenceBlocks'); found = referenceBlocks.indexOf(block) > -1; - dLog('adjacentTo', found, referenceBlocks, block); + if (trace_blockAdj) + dLog('adjacentTo', found, referenceBlocks, block); } return found; }, diff --git a/frontend/app/models/block.js b/frontend/app/models/block.js index 3b794b66a..d4ff3e718 100644 --- a/frontend/app/models/block.js +++ b/frontend/app/models/block.js @@ -314,7 +314,8 @@ export default DS.Model.extend({ parent = dataset && dataset.get('parent'), parentName = parent && parent.get('name'); // e.g. "myGenome" - dLog('referenceBlock', scope, dataset, reference, namespace, parent, parentName, parent && parent.get('id')); + if (trace_block) + dLog('referenceBlock', scope, dataset, reference, namespace, parent, parentName, parent && parent.get('id')); if (parent) { referenceBlock = this.get('store').peekAll('block') @@ -336,7 +337,8 @@ export default DS.Model.extend({ } return match;}) ; - dLog('referenceBlock', referenceBlock); + if (trace_block) + dLog('referenceBlock', referenceBlock); // expect referenceBlock.length == 0 or 1 if (referenceBlock.length !== undefined) referenceBlock = referenceBlock[0] || undefined; diff --git a/frontend/app/services/data/block.js b/frontend/app/services/data/block.js index 3b3e2ded1..031f65a35 100644 --- a/frontend/app/services/data/block.js +++ b/frontend/app/services/data/block.js @@ -738,7 +738,7 @@ export default Service.extend(Ember.Evented, { return map; }, new Map()); - if (trace_block) + if (trace_block > 1) log_Map('blocksByReference', map); return map; }), diff --git a/frontend/app/services/data/paths-progressive.js b/frontend/app/services/data/paths-progressive.js index 57470e9a2..3e0cf5608 100644 --- a/frontend/app/services/data/paths-progressive.js +++ b/frontend/app/services/data/paths-progressive.js @@ -36,11 +36,16 @@ function verifyFeatureRecord(fr, f) { frdv = [frdv]; if ((typeof(fv) == "number") || (frdv.length === undefined)) frdv = [fv]; + /** @return 1 if end of interval matches forward, -1 if reverse, 0 if not match. */ + function sameOrReverse(i) { return (frdv[i] === fv[i]) ? 1 : (frdv[i] === fv[1-i]) ? -1 : 0; } let + /** direction indicated by frdv[0]. */ + direction = sameOrReverse(0), + /** true if not an interval, or other end of interval matches in same direction. */ + sameDirection = (frdv.length < 2) || (sameOrReverse(1) === direction), same = (fr.id === f._id) && - (frdv[0] === fv[0]) && - ((frdv.length < 2) || (frdv[1] === fv[1])) && + direction && sameDirection && (frd.name === f.name); return same; } diff --git a/frontend/app/styles/app.scss b/frontend/app/styles/app.scss index dc013e15f..d102362a0 100644 --- a/frontend/app/styles/app.scss +++ b/frontend/app/styles/app.scss @@ -1335,7 +1335,9 @@ body > div.ember-popover { /* -------------------------------------------------------------------------- */ /* JSON editor, in RHS panel */ +#right-panel-content.right-panel-dataset .dataset-table { + width: inherit; margin-bottom: 0; } diff --git a/frontend/app/templates/components/panel/manage-dataset.hbs b/frontend/app/templates/components/panel/manage-dataset.hbs index 4122e2493..c9084a9a0 100644 --- a/frontend/app/templates/components/panel/manage-dataset.hbs +++ b/frontend/app/templates/components/panel/manage-dataset.hbs @@ -3,7 +3,7 @@ - + diff --git a/frontend/app/utils/domElements.js b/frontend/app/utils/domElements.js index 6392059cc..86c53c5d1 100644 --- a/frontend/app/utils/domElements.js +++ b/frontend/app/utils/domElements.js @@ -51,7 +51,8 @@ let let startX; let dragResize = d3.drag() // d3 v3: was .behavior .on('drag', function(d, i, g) { - logElementDimensions(g[0], 'on drag'); + if (trace_dom) + logElementDimensions(g[0], 'on drag'); // as for .resize() below, // .on() seems to apply a reasonable debounce, but if not, use Ember.run.debounce() From 346ed13e12601c178af15b87bd61cf99f9ac2e0d Mon Sep 17 00:00:00 2001 From: Don Date: Wed, 4 Mar 2020 16:32:04 +1100 Subject: [PATCH 05/16] re-add reference block to axis before data block this addresses re-adding after closing a split axis with tracks (#148, partially fixed in 9e9ccdf). draw-map.js : drawEffect() : add referencesFirst - sort viewed blocks by .referenceBlock. 14303d7 239649 Mar 4 15:30 frontend/app/components/draw-map.js --- frontend/app/components/draw-map.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index 4ad8062ae..69fe9fa30 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -1275,7 +1275,7 @@ export default Ember.Component.extend(Ember.Evented, { zd = oa.z[d]; let block = oa.z[key], match = (block.scope == zd.scope) && (block.dataset.get('name') == parentName); - dLog(key, block, match); + dLog(key, trace_stack ? block : block.dataset.get('name'), match); return match; } /** undefined if no parent found, otherwise is the id corresponding to parentName */ @@ -5982,8 +5982,14 @@ export default Ember.Component.extend(Ember.Evented, { let data = this.get('data'); Ember.run.throttle(function () { /** viewed[] is equivalent to data[], apart from timing differences. */ - let viewed = me.get('blockService.viewed'); - viewed.forEach((block) => me.oa.axisApi.ensureAxis(block.id)); + let viewed = me.get('blockService.viewed'), + /** create axes for the reference blocks before the data blocks are added. */ + referencesFirst = viewed.sort((a,b) => { + let aHasReference = !!a.get('referenceBlock'), + bHasReference = !!b.get('referenceBlock'); + return aHasReference === bHasReference ? 0 : aHasReference ? 1 : -1; + }); + referencesFirst.forEach((block) => me.oa.axisApi.ensureAxis(block.id)); me.draw(data, 'didRender'); }, 1500); @@ -6077,8 +6083,9 @@ export default Ember.Component.extend(Ember.Evented, { * 't[m] is not a function, at Object.applyStr (ember-utils.js:524)' */ resized : function(prevSize, currentSize) { + const trace_gui = 0; if (trace_gui) - console.log("resized in components/draw-map", this, prevSize, currentSize); + dLog("resized in components/draw-map", this, prevSize, currentSize); }, resize : function() { From a5499683502740bbeeec973465b788d490fbdb73 Mon Sep 17 00:00:00 2001 From: Don Date: Fri, 6 Mar 2020 18:52:14 +1100 Subject: [PATCH 06/16] resize height of right side of split axis and tracks axis-tracks.js : add resizeEffectHere(). draw-map.js : axisShowExtend() : use .merge to set up selections to update width & height move send(enableAxis2D) out of axisShowExtend(). Add axisShowExtendAll(). accept options.dualAxis. resizeEffect() : add result.changed using compareFields(). drop .on(resized), resized() - not needed, and to resolve the exception t[m] is not a function : axis-2d.js : add resized(). axis-tracks.hbs : use resizeEffectHere. axes-1d.hbs : fix axis-2d param drawMap. goto-feature-list.hbs : change feature search button text ->Blocks to Search. Object_filter.js : add compareFields(). frontend/app/ : 40f5586 12451 Mar 6 18:11 components/axis-2d.js 5c6b1dc 26571 Mar 6 16:49 components/axis-tracks.js 6ba912f 240506 Mar 6 18:24 components/draw-map.js f304ec8 706 Mar 5 17:51 templates/components/axis-tracks.hbs b274cf4 618 Mar 6 14:39 templates/components/draw/axes-1d.hbs e555d4b 1635 Mar 5 13:40 templates/components/goto-feature-list.hbs da26fbc 1601 Mar 6 17:13 utils/Object_filter.js --- frontend/app/components/axis-2d.js | 5 ++ frontend/app/components/axis-tracks.js | 10 +++ frontend/app/components/draw-map.js | 65 ++++++++++++------- .../app/templates/components/axis-tracks.hbs | 1 + .../app/templates/components/draw/axes-1d.hbs | 2 +- .../components/goto-feature-list.hbs | 2 +- frontend/app/utils/Object_filter.js | 35 +++++++++- 7 files changed, 95 insertions(+), 25 deletions(-) diff --git a/frontend/app/components/axis-2d.js b/frontend/app/components/axis-2d.js index 42a6c913a..0fce38375 100644 --- a/frontend/app/components/axis-2d.js +++ b/frontend/app/components/axis-2d.js @@ -253,6 +253,11 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { this.set('subComponents', []); }, + /** receive notification of draw-map resize. */ + resized : function(prevSize, currentSize) { + dLog("resized in components/axis-2d", this, prevSize, currentSize); + }, + didRender() { let me = this; let prevSize, currentSize; diff --git a/frontend/app/components/axis-tracks.js b/frontend/app/components/axis-tracks.js index efeb1ff3f..a3a525d38 100644 --- a/frontend/app/components/axis-tracks.js +++ b/frontend/app/components/axis-tracks.js @@ -672,6 +672,16 @@ export default InAxis.extend({ } return featuresLength; }), + resizeEffectHere : Ember.computed('resizeEffect', function () { + let result = this.get('resizeEffect'); + dLog('resizeEffectHere in axis-tracks', this.get('axisID'), result); + /** @return true if rc[f] indicates a change of field f. + * if the previous size is not recorded, then treat it as a change. + */ + function isChanged(rc, f) { return rc ? rc[f] : true; } + this.showResize(isChanged(result.changed, 'viewportWidth'), isChanged(result.changed, 'viewportHeight') /* , yScaleChanged ? */); + }), + keypress: function(event) { console.log("components/axis-tracks keypress", event); diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index 69fe9fa30..43cbb9d2c 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -37,7 +37,7 @@ import { updateRange } from '../utils/stacksLayout'; import {DragTransition, dragTransitionTime, dragTransitionNew, dragTransition } from '../utils/stacks-drag'; import { wheelNewDomain } from '../utils/draw/zoomPanCalcs'; import { round_2, checkIsNumber} from '../utils/domCalcs'; -import { Object_filter } from '../utils/Object_filter'; +import { Object_filter, compareFields } from '../utils/Object_filter'; import { name_chromosome_block, name_position_range, isOtherField } from '../utils/field_names'; import { breakPoint, breakPointEnableSet } from '../utils/breakPoint'; import { highlightFeature_drawFromParams } from './draw/highlight-feature'; @@ -330,7 +330,7 @@ export default Ember.Component.extend(Ember.Evented, { enableAxis2D: function(axisID, enabled) { - let axes1d = this.get('axes1d'); + let axes1d = this.get('axes1d') || this.get('stacks.axes1d'); let axis = axes1d[axisID]; if (axis === undefined) { @@ -462,7 +462,6 @@ export default Ember.Component.extend(Ember.Evented, { this.on('paths', addPathsToCollation); this.on('pathsByReference', addPathsByReferenceToCollation); - this.on('resized', function () { } ); } if (oa.showResize === undefined) @@ -557,6 +556,7 @@ export default Ember.Component.extend(Ember.Evented, { vc.calc(oa); if (vc.count > 1) { + /** could use equalFields(). */ let widthChanged = oa.vc.viewPort.w != oa.vc.viewPortPrev.w, heightChanged = oa.vc.viewPort.h != oa.vc.viewPortPrev.h; @@ -2251,14 +2251,14 @@ export default Ember.Component.extend(Ember.Evented, { .enter() .append("g") .attr("class", "axis-use"); - // merge / update ? + let em = ug.merge(eg); /** If dualAxis, use to show 2 identical axes. * Otherwise show only the left axis, and on the right side a line like an * axis with no ticks, just the top & bottom tick lines, but reflected so * that they point right. */ - let dualAxis = false; + let dualAxis = options && options.dualAxis; if (dualAxis) { let eu = eg /* extra "xlink:" seems required currently to work, refn : dsummersl - @@ -2271,9 +2271,10 @@ export default Ember.Component.extend(Ember.Evented, { .append("rect") .attr("x", 0) .attr("y", 0) - .attr("width", 0) + .attr("width", 0), + rm = er.merge(em.selectAll('g.axis-use > rect')) .attr("height", vc.yRange); - er + rm .transition().duration(1000) .attr("width", initialWidth); } @@ -2291,11 +2292,11 @@ export default Ember.Component.extend(Ember.Evented, { [+tickWidth, edgeHeight] ]), ra = eg - .append("path") + .append("path"), + rm = ra.merge(em.selectAll('g.axis-use > path')) + .transition().duration(1000) .attr("transform",function(d) {return "translate(" + (shiftRight + getAxisExtendedWidth(d)) + ",0)";}) .attr("d", sLine); - ra - .transition().duration(1000); } // foreignObject is case sensitive - refn https://gist.github.com/mbostock/1424037 @@ -2320,10 +2321,20 @@ export default Ember.Component.extend(Ember.Evented, { .append("div") .attr("id", "axis2D_" + axisID) // matches axis-2d:targetEltId() .style("border:1px green solid"); - - me.send('enableAxis2D', axisID, axis.extended); + } + function axisShowExtendAll() { + // based on collateO + dLog("axisShowExtendAll", oa.axisIDs.length, oa.axisIDs); + oa.stacks.axisIDs().forEach(function(d){ + let axisID = d, + axis = oa.axes[axisID]; + if (trace_stack > 1) + dLog(d, axisId2Name(d), axis); + axisShowExtend(axis, axisID, undefined); + }); } + if (trace_stack) { if (trace_stack > 1) @@ -5572,6 +5583,7 @@ export default Ember.Component.extend(Ember.Evented, { // toggle axis.extended, which is initially undefined. axis.extended = ! axis.extended; axisShowExtend(axis, axisName, undefined); + me.send('enableAxis2D', axisName, axis.extended); }); }); @@ -5763,10 +5775,13 @@ export default Ember.Component.extend(Ember.Evented, { /* This does .trigger() within .later(), which seems marginally better than vice versa; it works either way. (Planning to replace event:resize soon). */ if (widthChanged || heightChanged) try { + /** draw-map sends 'resized' event to listening sub-components using trigger(). + * It does not listen to this event. */ me.trigger('resized', widthChanged, heightChanged, useTransition); } catch (exc) { console.log('showResize', 'resized', me, me.resized, widthChanged, heightChanged, useTransition, graphDim, brushedDomains, exc.stack || exc); } + axisShowExtendAll(); showSynteny(oa.syntenyBlocks, undefined); }); }; @@ -6003,10 +6018,26 @@ export default Ember.Component.extend(Ember.Evented, { */ 'stacksWidthChanges.@each', 'viewportWidth', 'viewportHeight', function () { + let + stacksWidthChanges = this.get('stacksWidthChanges'), + viewportWidth = this.get('viewportWidth'), + viewportHeight = this.get('viewportHeight'), + result = { + stacksWidthChanges, viewportWidth, viewportHeight + }; + let prev = this.get('resizePrev'); + this.set('resizePrev', result); + if (prev) { + delete result.changed; + let changed = compareFields(prev, result, (a,b) => a !== b); + result.changed = changed; + } + dLog('resizeEffect', result); if (false) // currently the display is probably smoother with the debounce; later after tidying up the resize structure this direct call may be better. this.get('resize').apply(this.get('oa'), [/*transition*/true]); else Ember.run.debounce(this.get('oa'), this.get('resize'), [/*transition*/true], 500); + return result; }), /** for CP dependency. Depends on factors which affect the horizontal (X) layout of stacks. @@ -6078,16 +6109,6 @@ export default Ember.Component.extend(Ember.Evented, { return changed; }, - /** draw-map sends resized event to listening sub-components using trigger(). - * It does not listen to this event, or need to, but defining resized() may fix : - * 't[m] is not a function, at Object.applyStr (ember-utils.js:524)' - */ - resized : function(prevSize, currentSize) { - const trace_gui = 0; - if (trace_gui) - dLog("resized in components/draw-map", this, prevSize, currentSize); - }, - resize : function() { console.log("resize", this, arguments); /** when called via .observes(), 'this' is draw-map object. When called diff --git a/frontend/app/templates/components/axis-tracks.hbs b/frontend/app/templates/components/axis-tracks.hbs index 97f558442..1d4547d4c 100644 --- a/frontend/app/templates/components/axis-tracks.hbs +++ b/frontend/app/templates/components/axis-tracks.hbs @@ -1,4 +1,5 @@ {{!-- This is within div.devel-visible --}} +{{resizeEffectHere}}
showTrackBlocks : {{showTrackBlocks}}
{{#unless trackBlocks}} diff --git a/frontend/app/templates/components/draw/axes-1d.hbs b/frontend/app/templates/components/draw/axes-1d.hbs index e67d68760..0d790dd02 100644 --- a/frontend/app/templates/components/draw/axes-1d.hbs +++ b/frontend/app/templates/components/draw/axes-1d.hbs @@ -4,7 +4,7 @@ {{#draw/axis-1d drawMap=drawMap axis=axis axes2d=axes2d as |axis1d|}}
axis : {{axis1d.axis.id}}, {{axis1d.extended}}
{{#if axis1d.extended}} - {{axis-2d drawMap=this data=drawMap.oa axis1d=axis1d axisID=axis1d.axis.id axisObj=(hash axisID=axis1d.axis.id extended=axis1d.extended) }} + {{axis-2d drawMap=drawMap data=drawMap.oa axis1d=axis1d axisID=axis1d.axis.id axisObj=(hash axisID=axis1d.axis.id extended=axis1d.extended) }} {{/if}} {{draw/axis-ticks-selected axis1d=axis1d axisId=axis.id diff --git a/frontend/app/templates/components/goto-feature-list.hbs b/frontend/app/templates/components/goto-feature-list.hbs index e14fc58d4..ddd96f45a 100644 --- a/frontend/app/templates/components/goto-feature-list.hbs +++ b/frontend/app/templates/components/goto-feature-list.hbs @@ -33,7 +33,7 @@ icon='refresh' classColour="primary" }} -   → Blocks +   Search {{/elem/button-base}} {{!-- The above (if loading 'disabled') is not effective, so display diff --git a/frontend/app/utils/Object_filter.js b/frontend/app/utils/Object_filter.js index cbd9ec5ec..ecfc784c9 100644 --- a/frontend/app/utils/Object_filter.js +++ b/frontend/app/utils/Object_filter.js @@ -15,6 +15,39 @@ function Object_filter(obj, predicate) { } return result; }; + +/*----------------------------------------------------------------------------*/ + +/** Compare each field of a & b. + * Recurse into sub-objects. + * @param compareFn signature is (a, b) -> value (e.g. boolean) + * @return object with same keys as a & b, value of result.x is a.x === b.x + * @desc In the primary use case, keys(a) === keys(b), so this is assumed. + * The keys are taken from the 1st object (a). + * + * Usage example : + * let prev = this.get('resizePrev'); + * this.set('resizePrev', result); + * if (prev) { + * delete result.changed; + * let changed = compareFields(prev, result, (a,b) => a !== b); + * result.changed = changed; + * } + */ +function compareFields(a, b, compareFn) { + // related : isEqual from 'lodash/lang'; + let result = {}; + for (let k in a) { + if (! b || ! b[k]) + result[k] = undefined; + else if (typeof a[k] === "object") + result[k] = compareFields(a[k], b[k], compareFn); + else + result[k] = compareFn(a[k], b[k]); + } + return result; +}; + /*----------------------------------------------------------------------------*/ -export { Object_filter }; +export { Object_filter, compareFields }; From fab6de14d4d8aa7ba92e72b80f1e225006f436ac Mon Sep 17 00:00:00 2001 From: Don Date: Tue, 10 Mar 2020 15:55:27 +1100 Subject: [PATCH 07/16] improve horizontal layout of split axes. axis-2d.js : rectWidth() : use the x translation of when is not present because ! dualAxis. factor getUse() out of didInsertElement() so that it can be retried if axisUse.empty, with backoffTime. stacks.js : extendedWidth() : if this.extended is true, get width via axis2d.rectWidth(). frontend/app/ : 9f1c682 13145 Mar 10 15:31 components/axis-2d.js f33980b 73601 Mar 10 15:24 utils/stacks.js --- frontend/app/components/axis-2d.js | 33 ++++++++++++++++++++++++------ frontend/app/utils/stacks.js | 14 ++++++++++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/frontend/app/components/axis-2d.js b/frontend/app/components/axis-2d.js index 0fce38375..2e1115871 100644 --- a/frontend/app/components/axis-2d.js +++ b/frontend/app/components/axis-2d.js @@ -151,9 +151,19 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { rectWidth() { let axisUse = this.get('axisUse'), + dualAxis = false, // options && options.dualAxis + /** is present iff dualAxis. Otherwise use the x translation of */ rect2 = axisUse.select("g.axis-use > rect"), - width = rect2.attr('width'); - console.log("rectWidth", this.get('startWidth'), this.currentWidth(), rect2.node(), width); + path = axisUse.select('g.axis-use > path'), + width; + if (/*dualAxis*/ rect2.size()) { + width = rect2.attr('width'); + } else { + let transform = path.attr('transform'), + match = transform.match(/translate\(([0-9]+),/); + width = match && +match[1]; + } + console.log("rectWidth", this.get('startWidth'), this.currentWidth(), rect2.node(), path.node(), width); return width; }, currentWidth() { @@ -244,13 +254,24 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, { /*--------------------------------------------------------------------------*/ didInsertElement() { + this._super(...arguments); + + this.getUse(); + }, + getUse(backoffTime) { let oa = this.get('data'), axisUse = oa.svgContainer.selectAll("g.axis-outer#id"+this.get('axisID')), + /** is present iff dualAxis */ use = axisUse.selectAll("use"); - this.set('axisUse', axisUse); - this.set('use', use); - console.log("axis-2d didInsertElement", this, this.get('axisID'), axisUse.node(), use.node()); - this.set('subComponents', []); + if (axisUse.empty()) { + dLog('getUse', backoffTime); + Ember.run.later(() => this.getUse(backoffTime ? backoffTime * 2 : 1000)); + } else { + this.set('axisUse', axisUse); + this.set('use', use); + console.log("axis-2d didInsertElement", this, this.get('axisID'), axisUse.node(), use.node()); + this.set('subComponents', []); + } }, /** receive notification of draw-map resize. */ diff --git a/frontend/app/utils/stacks.js b/frontend/app/utils/stacks.js index e49612c62..a4cfa51ba 100644 --- a/frontend/app/utils/stacks.js +++ b/frontend/app/utils/stacks.js @@ -1895,8 +1895,20 @@ Stack.prototype.redrawAdjacencies = function () /** width of the axis. either 0 or .extended (current width of extension) */ Stacked.prototype.extendedWidth = function() { + let width = this.extended; + if (width === true) { + let childViews = Ember.get(this.axis1d, 'childViews'); + /** replace this with a passed parameter enabling axis-2d to report .width back up to axis-1d. */ + let axis2d = childViews && childViews.findBy( '_debugContainerKey', 'component:axis-2d'); + if (axis2d) { + width = axis2d.rectWidth(); + dLog('extendedWidth', this, childViews, axis2d, width); + } else + width = 120; + } + // dLog("Stacked extendedWidth()", this, this.extended); - return this.extended || 0; + return width || 0; }; /** @return range of widths, [min, max] of the Axes in this stack */ From 8b348cb096c8a8112e5c7f6b91a0707e10049ee4 Mon Sep 17 00:00:00 2001 From: Don Date: Tue, 10 Mar 2020 16:12:12 +1100 Subject: [PATCH 08/16] update reference, required for split axes. enableAxis2D() : if .axes1d not defined use reference from .stacks in oa. bfc02c0 240509 Mar 10 16:08 frontend/app/components/draw-map.js --- frontend/app/components/draw-map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index 43cbb9d2c..71d017978 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -330,7 +330,7 @@ export default Ember.Component.extend(Ember.Evented, { enableAxis2D: function(axisID, enabled) { - let axes1d = this.get('axes1d') || this.get('stacks.axes1d'); + let axes1d = this.get('axes1d') || this.get('oa.stacks.axes1d'); let axis = axes1d[axisID]; if (axis === undefined) { From 5d741947c7b077f93a1a73403000c091fa2bde0b Mon Sep 17 00:00:00 2001 From: Don Date: Tue, 10 Mar 2020 16:53:35 +1100 Subject: [PATCH 09/16] re-connect with extendedWidth for x ordinate of paths to split axes draw-map.js: inside(): use axis .extendedWidth() because extended is now just true. frontend/app/ : 8af9b30 240752 Mar 10 16:46 components/draw-map.js cc0b4d1 73630 Mar 10 16:46 utils/stacks.js --- frontend/app/components/draw-map.js | 5 ++++- frontend/app/utils/stacks.js | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index 71d017978..4331c55fa 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -3231,6 +3231,9 @@ export default Ember.Component.extend(Ember.Evented, { } /** Return the x positions of the given axes; if the leftmost is split, add * its width to the corresponding returned axis position. + * The purpose is to give the x positions which paths between the 2 axes + * should terminate at, hence the name 'inside' - it is concerned with the + * inside edges of the axes from the perspective of the space between them. * @param ak1, ak2 axis IDs (i.e. oa.axes[ak1] is Stacked, not Block) * @param cached true means use the "old" / cached positions o[ak], otherwise use the current scale x(ak). * @return 2 x-positions, in an array, in the given order (ak1, ak2). @@ -3250,7 +3253,7 @@ export default Ember.Component.extend(Ember.Evented, { if (aL.extended) { // console.log("inside", ak1, ak2, cached, xi, order, left, akL); - xi[left] += aL.extended; + xi[left] += aL.extendedWidth(); } return xi; } diff --git a/frontend/app/utils/stacks.js b/frontend/app/utils/stacks.js index a4cfa51ba..5287167e8 100644 --- a/frontend/app/utils/stacks.js +++ b/frontend/app/utils/stacks.js @@ -1902,7 +1902,8 @@ Stacked.prototype.extendedWidth = function() let axis2d = childViews && childViews.findBy( '_debugContainerKey', 'component:axis-2d'); if (axis2d) { width = axis2d.rectWidth(); - dLog('extendedWidth', this, childViews, axis2d, width); + if (trace_stack > 1) + dLog('extendedWidth', this, childViews, axis2d, width); } else width = 120; } From 2fbdfaca40de2697833cf0ff87e4e840df1b0e50 Mon Sep 17 00:00:00 2001 From: Don Date: Tue, 10 Mar 2020 20:03:32 +1100 Subject: [PATCH 10/16] after open/close split axis, update horizontal position of axes and connecting paths axis-1d.js : extendedEffect() : use .axisWidthResizeEnded(). 919fd90 28884 Mar 10 19:53 frontend/app/components/draw/axis-1d.js --- frontend/app/components/draw/axis-1d.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/draw/axis-1d.js b/frontend/app/components/draw/axis-1d.js index 156bbe1ad..e87481d3f 100644 --- a/frontend/app/components/draw/axis-1d.js +++ b/frontend/app/components/draw/axis-1d.js @@ -706,6 +706,9 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, { extended = this.get('extended'), axisID = this.get('axis.id'); dLog('extended', extended, axisID); + // possibly add this to axisAPi, or pass an action param. + this.drawMap.axisWidthResizeEnded(); + if (extended) this.removeTicks(); else @@ -713,8 +716,10 @@ export default Ember.Component.extend(Ember.Evented, AxisEvents, AxisPosition, { let axisID_t = [axisID, undefined]; this.renderTicksDebounce(axisID_t); } - /* .extended has changed, so the centre of the axisTitle is changed. */ - this.updateAxisTitleSize(); + + /* .extended has changed, so the centre of the axisTitle is changed. + * this.updateAxisTitleSize() is called in axisWidthResizeEnded()->stacksAdjust() + */ return extended; }), From 581a728c6981aa78ff7b0b39b9454c15dc0d4acc Mon Sep 17 00:00:00 2001 From: Don Date: Tue, 10 Mar 2020 20:04:59 +1100 Subject: [PATCH 11/16] adjustments to the calculation order of horizontal layout. Possibly not an essential part of the fix in the previous commit. adb549c 240902 Mar 10 19:22 frontend/app/components/draw-map.js --- frontend/app/components/draw-map.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index 4331c55fa..b2207caae 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -2661,7 +2661,8 @@ export default Ember.Component.extend(Ember.Evented, { .transition().duration(dragTransitionTime) ; - + oa.vc.calc(oa); + // vc.calc() calculates .axisXRange, which is used here. console.log('vc.axisXRange', vc.axisXRange, axisTitleS.nodes(), stacks.length); let axisXRange = vc.axisXRange; /** axisXRange[] already allows for 1/2 title space either side, so use length-1. @@ -2675,7 +2676,6 @@ export default Ember.Component.extend(Ember.Evented, { let titleText = vc.titleText || (vc.titleText = {}); oa.vc.axisHeaderTextLen = titlePx; - oa.vc.calc(oa); oa.axisTitleLayout.calc(axisSpacing, titlePx); @@ -5166,9 +5166,10 @@ export default Ember.Component.extend(Ember.Evented, { */ function stacksAdjust(changedNum, t) { + updateAxisTitleSize(undefined); + /* updateAxisTitleSize() uses vc.axisXRange but not o, so call it before collateO(). */ if (changedNum) collateO(); - updateAxisTitleSize(undefined); collateStacks(); if (changedNum) { From b6c0b0c6855ddcbaf26ebd5e4ab5886fbebce02e Mon Sep 17 00:00:00 2001 From: Don Date: Wed, 11 Mar 2020 14:20:56 +1100 Subject: [PATCH 12/16] updates to keep split axis right side path horizontal position aligned with path ends draw-map.js : stacksAdjust() : also call axisShowExtendAll(). stacks.js : extendedWidth() : in the default case use getAxisExtendedWidth() in place of the base width 120. frontend/app/ : 8e11887 241100 Mar 11 13:48 components/draw-map.js c5d26de 73928 Mar 11 14:18 utils/stacks.js --- frontend/app/components/draw-map.js | 4 ++++ frontend/app/utils/stacks.js | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/draw-map.js b/frontend/app/components/draw-map.js index b2207caae..bcae5e13b 100644 --- a/frontend/app/components/draw-map.js +++ b/frontend/app/components/draw-map.js @@ -2211,6 +2211,8 @@ export default Ember.Component.extend(Ember.Evented, { let p2 = this.parentNode.parentElement; return "#a" + p2.__data__; } + if (! oa.axisApi.getAxisExtendedWidth) + oa.axisApi.getAxisExtendedWidth = getAxisExtendedWidth; function getAxisExtendedWidth(axisID) { let axis = oa.axes[axisID], @@ -5185,6 +5187,8 @@ export default Ember.Component.extend(Ember.Evented, { if (oa.svgContainer) oa.stacks.forEach(function (s) { s.redrawAdjacencies(); }); } + // could limit this to axes for which dataBlocks has changed + axisShowExtendAll(); // pathUpdate() uses flow.g, which is set after oa.foreground. if (oa.foreground && ysLength()) { diff --git a/frontend/app/utils/stacks.js b/frontend/app/utils/stacks.js index 5287167e8..145efbbdc 100644 --- a/frontend/app/utils/stacks.js +++ b/frontend/app/utils/stacks.js @@ -1904,8 +1904,14 @@ Stacked.prototype.extendedWidth = function() width = axis2d.rectWidth(); if (trace_stack > 1) dLog('extendedWidth', this, childViews, axis2d, width); - } else - width = 120; + } else { + /** based on the path translate calculation in draw-map.js : axisShowExtend(); + * These can be integrated when moved to axis-2d. + * Also axis-tracks.js : @see layoutWidth() + */ + let shiftRight = 5; + width = shiftRight + stacks.oa.axisApi.getAxisExtendedWidth(this.axisName); + } } // dLog("Stacked extendedWidth()", this, this.extended); From 47c1e0ed5303c5c6fcba199ff7068524bb34c734 Mon Sep 17 00:00:00 2001 From: Don Date: Wed, 11 Mar 2020 16:12:46 +1100 Subject: [PATCH 13/16] while axis is open/split, request features/tracks according to current zoom domain and view panel (paths/features) samples/density sizeThreshold is disabled in this commit, so that the retrieved tracks are displayed without zoom being required; we can add a slider to adjust for minimum feature/track size. axis-tracks.js : add featuresForTrackBlocksRequestEffect(), disable sizeThreshold. frontend/app/ : f84031d 27533 Mar 11 15:58 components/axis-tracks.js 56a28b6 15355 Mar 11 15:39 models/block.js --- frontend/app/components/axis-tracks.js | 22 +++++++++++++++++++++- frontend/app/models/block.js | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/frontend/app/components/axis-tracks.js b/frontend/app/components/axis-tracks.js index a3a525d38..0fe65a834 100644 --- a/frontend/app/components/axis-tracks.js +++ b/frontend/app/components/axis-tracks.js @@ -386,8 +386,9 @@ export default InAxis.extend({ /** if zoomed in, tracks are not filtered by sizeThreshold. * The logic is : if the user is zooming in, they are interested in * features regardless of size, e.g. smaller than a pixel. + * [ sizeThreshold is disabled by setting to undefined, while we prototype how to select a sub-set of features to display ] */ - sizeThreshold = zoomed ? undefined : pxSize * 1/*5*/, + sizeThreshold = undefined, // zoomed ? undefined : pxSize * 1/*5*/, tracksLayout = regionOfTree(t, yDomain, sizeThreshold), data = tracksLayout.intervals; if (false) // actually need to sum the .layoutWidth for all blockId-s, plus the block offsets which are calculated below @@ -578,6 +579,23 @@ export default InAxis.extend({ /*--------------------------------------------------------------------------*/ + featuresForTrackBlocksRequestEffect : Ember.computed( + 'trackBlocksR.[]', + // either axis1d.domain or yDomain is probably sufficient dependency. + 'axis1d.domain', 'yDomain', + function () { + dLog('featuresForTrackBlocksRequestEffect'); + let trackBlocks = this.get('trackBlocksR'), + /** featuresForAxis() uses getBlockFeaturesInterval(), which is also used by + * models/axis-brush.js */ + blockFeatures = trackBlocks.map(function (b) { return b.get('featuresForAxis'); } ); + /* no return value - result is displayed by showTrackBlocks() with data + * collated by tracksTree(). */ + }), + + /*--------------------------------------------------------------------------*/ + + /** Not used; can be used in .hbs for trace, for comparison against the result * of showTrackBlocks(). */ blocksFeaturesLengths : Ember.computed( @@ -648,7 +666,9 @@ export default InAxis.extend({ }), showTrackBlocks: Ember.computed( 'tracksTree', 'yDomain.0', 'yDomain.1', 'axis1d.zoomed', 'axis1d.extended', 'axis1d.featureLength', + 'featuresForTrackBlocksRequestEffect', function() { + this.get('featuresForTrackBlocksRequestEffect'); let tracks = this.get('tracksTree'); let axis1d = this.get('axis1d'), zoomed = this.get('axis1d.zoomed'), diff --git a/frontend/app/models/block.js b/frontend/app/models/block.js index d4ff3e718..a9a709108 100644 --- a/frontend/app/models/block.js +++ b/frontend/app/models/block.js @@ -383,6 +383,8 @@ export default DS.Model.extend({ /** When block is added to an axis, request features, scoped by the axis * current position. + * As used in axis-tracks : when axis is open/split, request features in + * response to, and as defined by, zoom changes. */ featuresForAxis : Ember.computed('axis', function () { /** This could be split out into a separate layer, concerned with reactively From e817e3ca1b2f451bc2915b8b26a250e395323b02 Mon Sep 17 00:00:00 2001 From: Don Date: Wed, 11 Mar 2020 16:27:35 +1100 Subject: [PATCH 14/16] increment version 1.3 -> 2.0 7d8785e 2611 Mar 11 16:25 frontend/package.json 5fb0ec7 1920 Mar 11 16:25 package.json --- frontend/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 206c01f95..8add28d45 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "pretzel-frontend", - "version": "1.3.0", + "version": "2.0.0", "description": "Frontend code for Pretzel", "license": "MIT", "author": "", diff --git a/package.json b/package.json index a072bba51..2e576213d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pretzel", "private": true, - "version": "1.3.0", + "version": "2.0.0", "dependencies": { }, "repository" : From dd90578bf271bd6a83ac38d3923245aa8509a61a Mon Sep 17 00:00:00 2001 From: Don Date: Thu, 12 Mar 2020 16:59:46 +1100 Subject: [PATCH 15/16] ensure that options.splitAxes1 is on by default Previously this default only applied when url options were given. query-params.js : use default value {} for options, and set default options.splitAxes1 after that. block.js : parsedOptions() : correct name : queryParams{Service,} frontend/app/ : 7d76a5f 38906 Mar 12 16:45 services/data/block.js 6b17203 2250 Mar 12 11:14 services/query-params.js --- frontend/app/services/data/block.js | 6 +++++- frontend/app/services/query-params.js | 9 ++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/app/services/data/block.js b/frontend/app/services/data/block.js index 031f65a35..0445e3298 100644 --- a/frontend/app/services/data/block.js +++ b/frontend/app/services/data/block.js @@ -82,10 +82,14 @@ export default Service.extend(Ember.Evented, { queryParams: service('query-params'), params : Ember.computed.alias('queryParams.params'), + /** params.options is the value passed to &options= + * parsedOptions is the result of parsing the values from that into attributes. + * params.parsedOptions is just the parsed values, and queryParams.urlOptions has defaults added. + */ + parsedOptions : Ember.computed.alias('queryParams.urlOptions'), summaryTask : {}, - parsedOptions : Ember.computed.alias('queryParamsService.urlOptions'), /** Not required because findRecord() is used; * might later want this for other requests or calculation results, but can diff --git a/frontend/app/services/query-params.js b/frontend/app/services/query-params.js index 9ad436065..f74f12eb2 100644 --- a/frontend/app/services/query-params.js +++ b/frontend/app/services/query-params.js @@ -27,12 +27,15 @@ export default Service.extend(Ember.Evented, { let options_param = this.get('params.options'), options; if (options_param && (options = parseOptions(options_param))) { - /* splitAxes1 is now enabled by default. */ - if (! options.splitAxes1) - options.splitAxes1 = true; // alpha enables new features which are not yet robust. options.splitAxes |= options.alpha; } + else + options = {}; + /* splitAxes1 is now enabled by default. */ + if (! options.splitAxes1) + options.splitAxes1 = true; + return options; }), urlOptionsEffect: Ember.computed('urlOptions', function () { From 79f19552d6198f646b8644200cc2ed392c471b5b Mon Sep 17 00:00:00 2001 From: Don Date: Thu, 12 Mar 2020 21:38:34 +1100 Subject: [PATCH 16/16] move HandsOnTable licenseKey configuration out of the repo source to the build environment. paths-table.js: define useHandsOnTable true iff config.handsOnTableLicenseKey is defined. Use config.handsOnTableLicenseKey also in data-csv.js and table-brushed.js. environment.js : define .handsOnTableLicenseKey from the build process environment. frontend/app/components/ : b3ecd13 27207 Mar 12 20:13 panel/paths-table.js 4a3da6c 11880 Mar 12 20:19 panel/upload/data-csv.js 4178540 4250 Mar 12 20:13 table-brushed.js 9d0f45c 2541 Mar 12 21:35 frontend/config/environment.js --- frontend/app/components/panel/paths-table.js | 17 ++++++-------- .../app/components/panel/upload/data-csv.js | 10 ++++----- frontend/app/components/table-brushed.js | 6 ++--- frontend/config/environment.js | 22 +++++++++++++++++++ 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/frontend/app/components/panel/paths-table.js b/frontend/app/components/panel/paths-table.js index b3337f50c..fb2052e8c 100644 --- a/frontend/app/components/panel/paths-table.js +++ b/frontend/app/components/panel/paths-table.js @@ -8,6 +8,7 @@ const { inject: { service } } = Ember; import PathData from '../draw/path-data'; import { pathsResultTypes, pathsApiResultType, pathsResultTypeFor, featureGetFn, featureGetBlock } from '../../utils/paths-api'; import { eltClassName } from '../../utils/domElements'; +import config from '../../config/environment'; const dLog = console.debug; @@ -46,8 +47,10 @@ if (! d3.selection.prototype.moveToFront) { }; } -/** Switch to select either HandsOnTable or ember-contextual-table. */ -const useHandsOnTable = true; +/** Switch to select either HandsOnTable or ember-contextual-table. + * If licenseKey is given, use HandsOnTable. + */ +const useHandsOnTable = !!config.handsOnTableLicenseKey; /** id of element which will hold HandsOnTable. */ const hoTableId = 'paths-table-ho'; @@ -664,14 +667,8 @@ export default Ember.Component.extend({ sortIndicator: true, multiColumnSorting: true, - /* in the latest non-commercial version 6.2.2, multiColumnSorting is - * present but didn't work with 'multiColumnSorting: true'; - * it is fine for all other features used. - * The following line enables the app to work with versions after 6.2.2, - * refn : https://handsontable.com/blog/articles/2019/3/handsontable-drops-open-source-for-a-non-commercial-license - * This app is used for academic research. - */ - licenseKey: 'non-commercial-and-evaluation' + /* see comment re. handsOnTableLicenseKey in frontend/config/environment.js */ + licenseKey: config.handsOnTableLicenseKey }); let $ = Ember.$; diff --git a/frontend/app/components/panel/upload/data-csv.js b/frontend/app/components/panel/upload/data-csv.js index 02a979b5d..a99717c99 100644 --- a/frontend/app/components/panel/upload/data-csv.js +++ b/frontend/app/components/panel/upload/data-csv.js @@ -1,6 +1,8 @@ import Ember from 'ember'; import UploadBase from './data-base'; +import config from '../../../config/environment'; + export default UploadBase.extend({ table: null, selectedDataset: 'new', @@ -58,12 +60,8 @@ export default UploadBase.extend({ afterRemoveRow: function() { that.checkData(); }, - /* - * The following line enables the app to work with versions after 6.2.2, - * refn : https://handsontable.com/blog/articles/2019/3/handsontable-drops-open-source-for-a-non-commercial-license - * This app is used for academic research. - */ - licenseKey: 'non-commercial-and-evaluation' + /* see comment re. handsOnTableLicenseKey in frontend/config/environment.js */ + licenseKey: config.handsOnTableLicenseKey }); that.set('table', table); $('.nav-tabs a[href="#left-panel-upload"]').on('shown.bs.tab', function() { diff --git a/frontend/app/components/table-brushed.js b/frontend/app/components/table-brushed.js index 3b4c57d97..c7110d7b6 100644 --- a/frontend/app/components/table-brushed.js +++ b/frontend/app/components/table-brushed.js @@ -1,7 +1,7 @@ import Ember from 'ember'; import { eltClassName } from '../utils/domElements'; -// import Handsontable from 'handsontable'; +import config from '../config/environment'; /* global d3 */ /* global Handsontable */ @@ -108,8 +108,8 @@ export default Ember.Component.extend({ column: 2, sortOrder: true }, - // see comment in paths-table.js - licenseKey: 'non-commercial-and-evaluation' + /* see comment re. handsOnTableLicenseKey in frontend/config/environment.js */ + licenseKey: config.handsOnTableLicenseKey }); that.set('table', table); $("#table-brushed").on('mouseleave', function(e) { diff --git a/frontend/config/environment.js b/frontend/config/environment.js index 867ab49dd..2d0afe213 100644 --- a/frontend/config/environment.js +++ b/frontend/config/environment.js @@ -1,5 +1,7 @@ /* jshint node: true */ +/* global module */ +/* global process */ module.exports = function(environment) { var ENV = { @@ -10,6 +12,8 @@ module.exports = function(environment) { apiNamespace: 'api', // adding to the host for API calls rootURL: '/', // used with Ember local routing locationType: 'auto', + handsOnTableLicenseKey: null, + EmberENV: { FEATURES: { // Here you can enable experimental features on an ember canary build @@ -54,6 +58,24 @@ module.exports = function(environment) { if (environment === 'production') { ENV.apiHost = ''; } + /** If handsOnTableLicenseKey is defined in the environment of npm / ember, + * HandsOnTable is used for the spreadsheet-style tables in : + * components/panel/paths-table.js + * components/panel/upload/data-csv.js + * components/table-brushed.js + * otherwise ember-contextual-table is used. + * + * In the last non-commercial HandsOnTable version 6.2.2, multiColumnSorting + * is present but didn't work with 'multiColumnSorting:true'; it is fine for + * all other features used. To use this version, change "handsontable" + * version dependency in frontend/bower.json (later this will be in + * package.json) + * + * Also see : https://handsontable.com/blog/articles/2019/3/handsontable-drops-open-source-for-a-non-commercial-license + * https://handsontable.com/docs/7.4.2/tutorial-license-key.html + */ + ENV.handsOnTableLicenseKey = process.env.handsOnTableLicenseKey; + return ENV; };
Namespace{{dataset.namespace}}
Namespace{{dataset.namespace}}
Parent{{dataset.parent.id}}
Created{{formatDate dataset.createdAt}}
Last Updated{{formatDate dataset.updatedAt}}