1. 1 : /**
  2. 2 : * CustomSet is a tool for creating a layout of other tools.
  3. 3 : *
  4. 4 : * @class CustomSet
  5. 5 : * @memberof Tools
  6. 6 : */
  7. 7 : Ext.define('Voyant.panel.CustomSet', {
  8. 8 : extend: 'Ext.panel.Panel',
  9. 9 : mixins: ['Voyant.panel.Panel'],
  10. 10 : alias: 'widget.customset',
  11. 11 : statics: {
  12. 12 : i18n: {
  13. 13 : },
  14. 14 : api: {
  15. 15 : /**
  16. 16 : * @memberof Tools.CustomSet
  17. 17 : * @instance
  18. 18 : * @property {String} layout A border layout string, of the format expected by <a target="_blank" href="https://docs.sencha.com/extjs/6.2.0/classic/Ext.layout.container.Border.html">Border layout</a>
  19. 19 : */
  20. 20 : layout: undefined,
  21. 21 :
  22. 22 : /**
  23. 23 : * @memberof Tools.CustomSet
  24. 24 : * @instance
  25. 25 : * @property {String} tableLayout A table layout string, usually generated by the <a target="_blank" href="https://voyant-tools.org/builder/">Skin Builder</a>
  26. 26 : */
  27. 27 : tableLayout: undefined
  28. 28 : },
  29. 29 : glyph: 'xf17a@FontAwesome'
  30. 30 : },
  31. 31 : header: false,
  32. 32 : height: '100%',
  33. 33 : width: '100%',
  34. 34 :
  35. 35 : constructor: function() {
  36. 36 : this.mixins['Voyant.util.Api'].constructor.apply(this, arguments); // force api load
  37. 37 : this.callParent(arguments);
  38. 38 : this.mixins['Voyant.panel.Panel'].constructor.apply(this, arguments);
  39. 39 : },
  40. 40 :
  41. 41 : initComponent: function() {
  42. 42 : if (this.getApiParam('layout')) {
  43. 43 : Ext.apply(this,{
  44. 44 : layout: 'border',
  45. 45 : items: []
  46. 46 : })
  47. 47 : } else if (this.getApiParam('tableLayout')) {
  48. 48 : this.initTableLayout();
  49. 49 : }
  50. 50 : this.callParent()
  51. 51 : },
  52. 52 :
  53. 53 : listeners: {
  54. 54 : loadedCorpus: function(src, corpus) {
  55. 55 : if (this.getApiParam('layout')) { // not sure why, but we seem to need to fire event for child panels
  56. 56 : this.query("panel").forEach(function(p) {
  57. 57 : p.fireEvent("loadedCorpus", src, corpus);
  58. 58 : })
  59. 59 : }
  60. 60 : },
  61. 61 : boxready: function(panel) {
  62. 62 : if (this.getApiParam('layout')) {
  63. 63 : this.initBorderLayoutComponents();
  64. 64 : } else if (this.getApiParam('tableLayout')) {
  65. 65 : this.doTableSizing();
  66. 66 : this.on('resize', function(panel, newwidth, newheight, oldwidth, oldheight) {
  67. 67 : if (oldwidth !== undefined && oldheight !== undefined) {
  68. 68 : var widthRatio = newwidth/oldwidth;
  69. 69 : var heightRatio = newheight/oldheight;
  70. 70 : this.doTableSizing(widthRatio, heightRatio);
  71. 71 : }
  72. 72 : }, this);
  73. 73 : } else {
  74. 74 : this.showError(this.localize('noLayoutSpecified'))
  75. 75 : }
  76. 76 : }
  77. 77 :
  78. 78 : },
  79. 79 :
  80. 80 : initBorderLayoutComponents: function() {
  81. 81 : var layoutString = decodeURI(this.getApiParam('layout'))
  82. 82 : .replace(/r1/g, 'region')
  83. 83 : .replace(/i1/g, 'items')
  84. 84 : .replace(/s1/g, 'split')
  85. 85 : .replace(/c1/g, 'collapsible')
  86. 86 : .replace(/c2/g, 'collapsed')
  87. 87 : .replace(/w1/g, 'width')
  88. 88 : .replace(/h1/g, 'height')
  89. 89 : .replace(/p1/g, '%')
  90. 90 : .replace(/"x1":"/g, '"xtype":"')
  91. 91 : .replace(/c3/g, 'center')
  92. 92 : .replace(/n1/g, 'north')
  93. 93 : .replace(/e1/g, 'east')
  94. 94 : .replace(/s2/g, 'south')
  95. 95 : .replace(/w2/g, 'west')
  96. 96 : .replace(/"xtype":"(\w+)"/g, function(match, tool) {
  97. 97 : if (!Ext.ClassManager.getByAlias("widget."+tool.toLowerCase())) {
  98. 98 : if (tool=="Links") {tool="CollocatesGraph";}
  99. 99 : else if (tool=="CorpusGrid") {tool="Documents";}
  100. 100 : else if (tool=="CorpusSummary") {tool="Summary";}
  101. 101 : else if (tool=="CorpusTypeFrequenciesGrid") {tool="CorpusTerms";}
  102. 102 : else if (tool=="DocumentInputAdd") {tool="CorpusTerms";}
  103. 103 : else if (tool=="DocumentTypeCollocateFrequenciesGrid") {tool="CorpusTerms";}
  104. 104 : else if (tool=="DocumentTypeFrequenciesGrid") {tool="DocumentTerms";}
  105. 105 : else if (tool=="DocumentTypeKwicsGrid") {tool="Contexts";}
  106. 106 : else if (tool=="TypeFrequenciesChart") {tool="Trends";}
  107. 107 : else if (tool=="VisualCollocator") {tool="CollocatesGraph";}
  108. 108 : else {tool="NoTool"}
  109. 109 : }
  110. 110 : return '"xtype":"'+tool.toLowerCase()+'"'+(tool=="NoTool" ? ',"html":"'+new Ext.Template(panel.localize('noSuchTool')).applyTemplate([tool])+'"' : '')
  111. 111 : })
  112. 112 :
  113. 113 : var items;
  114. 114 : try {
  115. 115 : items = Ext.decode(layoutString);
  116. 116 : } catch (e) {
  117. 117 : items = {region: 'center', html: '<div>Error constructing layout:'+e+'</div>'};
  118. 118 : }
  119. 119 :
  120. 120 : if (items == null) {
  121. 121 : items = {region: 'center', html: '<div>Error: no layout information found.</div>'}
  122. 122 : }
  123. 123 :
  124. 124 : this.addBorderLayouts(items);
  125. 125 :
  126. 126 : this.on("add", function(custom, cmp) {
  127. 127 : cmp.on("boxready", function(cmp) {
  128. 128 : // cmp.query("panel").forEach(function(p) {
  129. 129 : // custom;
  130. 130 : //// debugger
  131. 131 : // })
  132. 132 : })
  133. 133 : })
  134. 134 : this.add(items);
  135. 135 : // .on("boxready", function() {
  136. 136 : // debugger
  137. 137 : // if (this.getCorpus()) { // we may have loaded the corpus after the layout, so refire the event
  138. 138 : // this.getApplication().dispatchEvent("loadedCorpus", this.getApplication(), corpus);
  139. 139 : // }
  140. 140 : // })
  141. 141 :
  142. 142 : },
  143. 143 :
  144. 144 : addBorderLayouts: function(items) {
  145. 145 : var size = Ext.getBody().getSize();
  146. 146 : for (var i = 0; i < items.length; i++) {
  147. 147 : var item = items[i];
  148. 148 : if (Ext.isString(item.width)) {
  149. 149 : item.width = Math.round(size.width * parseInt(item.width) / 100);
  150. 150 : } else if (Ext.isString(item.height)) {
  151. 151 : item.height = Math.round(size.height * parseInt(item.height) / 100);
  152. 152 : }
  153. 153 : if (item.items && item.items.length > 1) {
  154. 154 : item.layout = 'border';
  155. 155 : this.addBorderLayouts(item.items);
  156. 156 : } else {
  157. 157 : item.layout = 'fit';
  158. 158 : }
  159. 159 : }
  160. 160 : },
  161. 161 :
  162. 162 : initTableLayout: function() {
  163. 163 : Ext.suspendLayouts();
  164. 164 : var tableLayout = decodeURI(this.getApiParam('tableLayout'));
  165. 165 :
  166. 166 : if (tableLayout && tableLayout.charAt(0)!="{" && tableLayout.charAt(0)!="[") {
  167. 167 : var cells = [];
  168. 168 : tableLayout.replace(/;/g,",").split(/,\s*/).forEach(function(cell) {
  169. 169 : cells.push(/^"'/.test(cell) ? cell : '"'+cell+'"');
  170. 170 : });
  171. 171 : tableLayout = "["+cells.join(",")+"]"; // treat as simple comma-separated string
  172. 172 : }
  173. 173 : var layout = Ext.decode(tableLayout);
  174. 174 : if (Ext.isArray(layout)) {
  175. 175 : layout = {
  176. 176 : cells: layout
  177. 177 : };
  178. 178 : }
  179. 179 : if (!layout.numCols && layout.cells && Ext.isArray(layout.cells)) {
  180. 180 : if (layout.cells.length < 3) {
  181. 181 : layout.numCols = layout.cells.length;
  182. 182 : } else if (layout.cells.length < 5) {
  183. 183 : layout.numCols = Math.ceil(layout.cells.length / 2);
  184. 184 : } else {
  185. 185 : layout.numCols = Math.ceil(layout.cells.length / 3);
  186. 186 : }
  187. 187 : }
  188. 188 : if (layout.numCols != null && layout.cells && Ext.isArray(layout.cells)) {
  189. 189 : var items = [];
  190. 190 : for (var i = 0; i < layout.cells.length; i++) {
  191. 191 : var cell = layout.cells[i];
  192. 192 : if (Ext.isObject(cell)) {
  193. 193 : cell.cellWidth = parseFloat(cell.width) || undefined;
  194. 194 : cell.cellHeight = parseFloat(cell.height) || undefined;
  195. 195 : delete cell.width;
  196. 196 : delete cell.height;
  197. 197 : items.push(cell);
  198. 198 : } else if (Ext.isArray(cell)) {
  199. 199 : var colspan = 1, rowspan = 1; xtype = undefined;
  200. 200 : if (cell[0] && Ext.isNumber(cell[0])) {
  201. 201 : colspan = cell[0];
  202. 202 : cell.shift();
  203. 203 : }
  204. 204 : if (cell[0] && Ext.isString(cell[0])) {
  205. 205 : xtype = cell[0].toLowerCase();
  206. 206 : cell.shift();
  207. 207 : }
  208. 208 : if (cell[0] && Ext.isNumber(cell[0])) {
  209. 209 : rowspan = cell[0];
  210. 210 : }
  211. 211 : if (xtype) {
  212. 212 : items.push({
  213. 213 : colspan: colspan,
  214. 214 : rowspan: rowspan,
  215. 215 : xtype: xtype
  216. 216 : })
  217. 217 : }
  218. 218 : } else if (Ext.isString(cell)) {
  219. 219 : items.push({
  220. 220 : xtype: cell.toLowerCase(),
  221. 221 : colspan: 1,
  222. 222 : rowspan: 1
  223. 223 : })
  224. 224 : }
  225. 225 : }
  226. 226 : Ext.apply(this, {
  227. 227 : layout: {
  228. 228 : type: 'table',
  229. 229 : width: '100%',
  230. 230 : height: '100%',
  231. 231 : columns: layout.numCols,
  232. 232 : tableAttrs: {
  233. 233 : style: {
  234. 234 : width: '100%',
  235. 235 : height: '100%'
  236. 236 : }
  237. 237 : },
  238. 238 : tdAttrs: {
  239. 239 : style: {
  240. 240 : padding: '0px',
  241. 241 : verticalAlign: 'top'
  242. 242 : }
  243. 243 : }
  244. 244 : },
  245. 245 : defaults: { // place holder values to ensure that the children are rendered
  246. 246 : width: 10,
  247. 247 : height: 10,
  248. 248 : border: true
  249. 249 : },
  250. 250 : items: items
  251. 251 : });
  252. 252 : } else {
  253. 253 : this.showError("badTableLayoutDefinition")
  254. 254 : }
  255. 255 :
  256. 256 : Ext.resumeLayouts();
  257. 257 : },
  258. 258 : doTableSizing: function(widthRatio, heightRatio) {
  259. 259 : var sizeMap = {};
  260. 260 :
  261. 261 : var table = this.getTargetEl().down(".x-table-layout");
  262. 262 : var tableSize = table.getSize(false);
  263. 263 :
  264. 264 : var rows = table.dom.rows;
  265. 265 : for (var i=0; i<rows.length; i++) {
  266. 266 : var cells = rows[i].cells;
  267. 267 : for (var j=0; j<cells.length; j++) {
  268. 268 : var cell = cells[j];
  269. 269 : var cellEl = Ext.get(cell);
  270. 270 : var panelEl = cellEl.down('.x-panel');
  271. 271 : var cmpId = panelEl.id;
  272. 272 :
  273. 273 : var size;
  274. 274 : if (widthRatio !== undefined && heightRatio !== undefined) {
  275. 275 : size = panelEl.getSize(false);
  276. 276 : size.width = size.width * widthRatio;
  277. 277 : size.height = size.height * heightRatio;
  278. 278 : // FIXME multiple resize calls gradually reduce size
  279. 279 : } else {
  280. 280 : var sizeObj = cellEl.getSize(false);
  281. 281 :
  282. 282 : var cmp = Ext.getCmp(cmpId);
  283. 283 : var widthPercent = cmp.initialConfig.cellWidth;
  284. 284 : var heightPercent = cmp.initialConfig.cellHeight;
  285. 285 :
  286. 286 : if (widthPercent !== undefined) {
  287. 287 : sizeObj.width = tableSize.width * (widthPercent/100);
  288. 288 : cellEl.setWidth(sizeObj.width);
  289. 289 : }
  290. 290 : if (heightPercent !== undefined) {
  291. 291 : sizeObj.height = tableSize.height * (heightPercent/100);
  292. 292 : cellEl.setHeight(sizeObj.height);
  293. 293 : }
  294. 294 :
  295. 295 : size = sizeObj;
  296. 296 : }
  297. 297 :
  298. 298 : sizeMap[cmpId] = size;
  299. 299 : }
  300. 300 : }
  301. 301 :
  302. 302 : for (var id in sizeMap) {
  303. 303 : var size = sizeMap[id];
  304. 304 : Ext.getCmp(id).setSize(size);
  305. 305 : }
  306. 306 :
  307. 307 : this.updateLayout();
  308. 308 : }
  309. 309 : })