From 0810e7b3a9240622db09f52c5d72d6985414b024 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 24 Jul 2018 15:22:45 +0300 Subject: [PATCH 01/18] UITable: added a utility function that converts cell [r,c] to JS ids. This was tested on tested on R2018a. The code below demonstrates what the new function does: ```matlab % Create figure: uiFig = uifigure('Position', [100 100 800 130]); mlapptools.setTimeout(uiFig,15); hT(1) = uitable(uiFig,'Data', magic(3), 'Position', [020 020 300 80]); hT(2) = uitable(uiFig,'Data', magic(4), 'Position', [350 010 400 100]); % Get IDs: ID1 = mlapptools.getTableCellID(hT(1), 1:3, 1:3 ); ID2 = mlapptools.getTableCellID(hT(2), 1:4, 3*ones(1,4)); pause(5); % Wait to ensure tables are ready. % Apply styles: hWW = mlapptools.getWebWindow(uiFig); arrayfun(@(x)mlapptools.setStyle(hWW, 'background', 'red', x), ID1); arrayfun(@(x)mlapptools.setStyle(hWW, 'background', 'yellow', x), ID2); ``` --- mlapptools.m | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/mlapptools.m b/mlapptools.m index a4c91e9..068be72 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -171,6 +171,50 @@ function fontWeight(uiElement, weight) % "Clear" the temporary JS variable win.executeJS('W = undefined'); end % getParentNodeID + + function [ID_obj] = getTableCellID(hTable, r, c) + % This method returns one or more ID objects, corresponding to specific cells in a + % uitable, as defined by the cells' row and column indices. + %% Constants: + TABLE_CLASS_NAME = 'matlab.ui.control.Table'; + CELL_ID_PREFIX = {'variableeditor_views_editors_UITableEditor_'}; + %% Input tests: + assert( isa(hTable, TABLE_CLASS_NAME) && ishandle(hTable), 'Invalid uitable handle!'); + nR = numel(r); nC = numel(c); + assert( nR == nC, 'The number of elements in r and c must be the same!'); + sz = size(hTable.Data); + assert( all(r <= sz(1)), 'One or more requested rows are out-of-bounds!'); + assert( all(c <= sz(2)), 'One or more requested columns are out-of-bounds!'); + %% Computing the offset + % If there's more than one uitable in the figure, IDs are assigned + % consecutively. For example, in the case of a 3x3 and 4x4 arrays, + % where the smaller table was created first, IDs will assigned as follows: + % + % [ 0 1 2 [ 9 10 11 12 + % 3 4 5 13 14 15 16 + % 6 7 8] 17 18 19 20 + % 21 22 23 24] + % + % ... which is why if want to change the 2nd table we need to offset the + % IDs by the total amount of elements in all preceding arrays, which is 9. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Get siblings (flipping to negate that newer children appear first) + hC = flip(hTable.Parent.Children); + % Find which of them is a table: + hC = hC( arrayfun(@(x)isa(x, TABLE_CLASS_NAME), hC) ); + % Find the position of the current table in the list, and count the elements of + % all preceding tables: + offset = sum(arrayfun(@(x)numel(x.Data), hC(1:find(hC == hTable)-1))); + % Compute indices, r and c are reversed due to row-major ordering of the IDs + idx = sub2ind(sz, c, r) + offset - 1; % -1 because JS IDs are 0-based + idc = strcat(CELL_ID_PREFIX, num2str(idx(:))); + % Preallocation: + ID_obj(nR,1) = WidgetID; + % ID array population: + for indI = 1:numel(idx) + ID_obj(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, idc{indI} ); + end + end % getTableCellID function [win, widgetID] = getWebElements(uiElement) % A method for obtaining the webwindow handle and the widget ID corresponding @@ -488,7 +532,7 @@ function unlockUIFig(hUIFig) ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); warning(warnState); % Restore warning state hFig = hFigs(hWebwindow == ww); - end % figFromWebwindow + end % figFromWebwindow function [ID_obj] = getWidgetID(win, data_tag) % This method returns a structure containing some uniquely-identifying information @@ -667,6 +711,12 @@ function waitTillWebwindowLoaded(hWebwindow, hFig) hWebwindow.executeJS('require(["dojo/ready"], function(ready){});'); end end % waitTillWebwindowLoaded + + function htmlRootPath = getFullPathFromWW(win) + % Get a local href value, e.g. from an included main.css + href = win.executeJS('document.body.getElementsByTagName("link")[0].href'); + htmlRootPath = win.executeJS(['var link = document.createElement("a"); link.href = ' href ';']); + end end % Private Static Methods From 38c49bd9501be90dab3b3ca94786796d420bccb5 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 25 Jul 2018 17:53:05 +0300 Subject: [PATCH 02/18] Stabilized waitTillWebwindowLoaded() Added some mechanisms to reduce the likelihood of errors in getWebElements. --- mlapptools.m | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index eebcd29..0b357ce 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -177,7 +177,7 @@ function fontWeight(uiElement, weight) % to the provided uifigure control. % Get a handle to the webwindow win = mlapptools.getWebWindow(uiElement); - mlapptools.waitTillWebwindowLoaded(win); + mlapptools.waitTillWebwindowLoaded(win, ancestor(uiElement, 'matlab.ui.Figure') ); % Find which element of the DOM we want to edit switch uiElement.Type @@ -592,8 +592,12 @@ function unlockUIFig(hUIFig) end % getWidgetIDFromDijit function to = getTimeout(hFig) - to = getappdata(hFig, mlapptools.TAG_TIMEOUT); - if isempty(to), to = mlapptools.QUERY_TIMEOUT; end + if isempty(hFig) || ~isa(hFig, 'matlab.ui.Figure') + to = mlapptools.QUERY_TIMEOUT; + else + to = getappdata(hFig, mlapptools.TAG_TIMEOUT); + if isempty(to), to = mlapptools.QUERY_TIMEOUT; end + end end % getTimeout function tf = isUIFigure(hList) @@ -698,11 +702,14 @@ function waitTillFigureLoaded(hFig) function waitTillWebwindowLoaded(hWebwindow, hFig) % A blocking method that ensures a certain webwindow has fully loaded. - if nargin < 2 - hFig = mlapptools.figFromWebwindow(hWebwindow); + try + if nargin < 2 + hFig = mlapptools.figFromWebwindow(hWebwindow); + end + to = mlapptools.getTimeout(hFig); + catch % possible workaround for R2017a: + to = mlapptools.getTimeout([]); end - - to = mlapptools.getTimeout(hFig); tic while (toc < to) && ~jsondecode(hWebwindow.executeJS(... 'this.hasOwnProperty("require") && require !== undefined && typeof(require) === "function"')) From d25f7e82bb87a5ec4ba24ae17585f4dd41bb1a27 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 25 Jul 2018 18:15:03 +0300 Subject: [PATCH 03/18] Added support in setStyle for non-scalar IDs It is now possible to apply the same style to multiple elements "in one go". --- mlapptools.m | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 068be72..9c2c07e 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -356,16 +356,21 @@ function fontWeight(uiElement, weight) ID_obj = varargin{4}; end - styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... - ID_obj.ID_attr, ID_obj.ID_val, styleAttr, styleValue); - % ^ this might result in junk if widgetId=='null'. - try - win.executeJS(styleSetStr); - % ^ this might crash in case of invalid styleAttr/styleValue. - catch ME - % Test for "Invalid or unexpected token": - ME = mlapptools.checkJavascriptSyntaxError(ME, styleSetStr); - rethrow(ME); + % Handle the case of a non-scalar ID_obj recursively: + if ~isscalar(ID_obj) + arrayfun(@(x)mlapptools.setStyle(win, styleAttr, styleValue, x), ID_obj); + else + styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... + ID_obj.ID_attr, ID_obj.ID_val, styleAttr, styleValue); + % ^ this might result in junk if widgetId=='null'. + try + win.executeJS(styleSetStr); + % ^ this might crash in case of invalid styleAttr/styleValue. + catch ME + % Test for "Invalid or unexpected token": + ME = mlapptools.checkJavascriptSyntaxError(ME, styleSetStr); + rethrow(ME); + end end % Assign outputs: From 02787288c25d8dc8f3f48a4b52651cb67c038378 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Wed, 25 Jul 2018 18:23:00 +0300 Subject: [PATCH 04/18] Cosmetic changes to the code - Removed annoying empty spaces at the ends of lines. - Set tab width to 2. --- mlapptools.m | 1421 +++++++++++++++++++++++++------------------------- 1 file changed, 710 insertions(+), 711 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 9c2c07e..bd4b38b 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -1,730 +1,729 @@ classdef (Abstract) mlapptools - % MLAPPTOOLS is a collection of static methods for customizing the - % R2016a-introduced web-based uifigure windows and associated UI elements - % through DOM manipulations. - % - % MLAPPTOOLS' public methods: - % - % aboutJSLibs - Return version information about certain JS libraries. - % fontColor - Modify font color. - % fontWeight - Modify font weight. - % getHTML - Return the full HTML code of a uifigure. - % getWebElements - Extract a webwindow handle and a widget ID from a uifigure control handle. - % getWebWindow - Extract a webwindow handle from a uifigure handle. - % getWidgetInfo - Gather information about a specific dijit widget. - % getWidgetList - Gather information about all dijit widget in a specified uifigure. - % setStyle - Modify a specified style property. - % setTimeout - Override the default timeout for dojo commands, for a specific uifigure. - % textAlign - Modify text alignment. - % unlockUIFig - Allow the uifigure to be opened using an external browser. - % waitForFigureReady - A blocking method that only returns after the uifigure is fully loaded. - % - % See README.md for detailed documentation and examples. - - - properties (Access = private, Constant = true) - QUERY_TIMEOUT = 5; % Dojo query timeout period, seconds - TAG_TIMEOUT = 'QUERY_TIMEOUT'; - DEF_ID_ATTRIBUTE = 'id'; - end - - methods (Access = public, Static = true) + % MLAPPTOOLS is a collection of static methods for customizing the + % R2016a-introduced web-based uifigure windows and associated UI elements + % through DOM manipulations. + % + % MLAPPTOOLS' public methods: + % + % aboutJSLibs - Return version information about certain JS libraries. + % fontColor - Modify font color. + % fontWeight - Modify font weight. + % getHTML - Return the full HTML code of a uifigure. + % getWebElements - Extract a webwindow handle and a widget ID from a uifigure control handle. + % getWebWindow - Extract a webwindow handle from a uifigure handle. + % getWidgetInfo - Gather information about a specific dijit widget. + % getWidgetList - Gather information about all dijit widget in a specified uifigure. + % setStyle - Modify a specified style property. + % setTimeout - Override the default timeout for dojo commands, for a specific uifigure. + % textAlign - Modify text alignment. + % unlockUIFig - Allow the uifigure to be opened using an external browser. + % waitForFigureReady - A blocking method that only returns after the uifigure is fully loaded. + % + % See README.md for detailed documentation and examples. + + properties (Access = private, Constant = true) + QUERY_TIMEOUT = 5; % Dojo query timeout period, seconds + TAG_TIMEOUT = 'QUERY_TIMEOUT'; + DEF_ID_ATTRIBUTE = 'id'; + end + + methods (Access = public, Static = true) + + function [jsLibVersions] = aboutJSLibs() + % A method for getting version info about some JS libararies visible to MATLAB. + % This includes the Dojo Toolkit and ReactJS. - function [jsLibVersions] = aboutJSLibs() - % A method for getting version info about some JS libararies visible to MATLAB. - % This includes the Dojo Toolkit and ReactJS. - - % Test if a *valid* webwindow already exists: - % (Written for compatibility with older releases) - exWW = matlab.internal.webwindowmanager.instance.findAllWebwindows(); - validWinID = find(~cellfun(@isempty,strfind({exWW.URL}.','uifigure')),... - 1, 'first'); %#ok - if isempty(validWinID) - f = uifigure; drawnow; tmpWindowCreated = true; - winID = numel(exWW) + 1; - else - tmpWindowCreated = false; - winID = validWinID; - end - - dojoVersion = matlab.internal.webwindowmanager.instance ... - .windowList(winID).executeJS('dojo.version'); - - reactVersion = matlab.internal.webwindowmanager.instance ... - .windowList(winID).executeJS(... - 'require("react/react.min").version;'); - - if tmpWindowCreated - delete(f); - end - - % If MATLAB is sufficiently new, convert the JSON to a struct: - if str2double(subsref(ver('matlab'), substruct('.','Version'))) >= 9.1 %R2016b - dv = jsondecode(dojoVersion); - dojoVersion = char(strrep(join(string(struct2cell(dv)),'.'),'..','.')); - reactVersion = jsondecode(reactVersion); - else - dojoVersion = strsplit(dojoVersion,{':',',','"','}','{'}); - dojoVersion = char(strjoin(dojoVersion([3,5,7,10]),'.')); - reactVersion = reactVersion(2:end-1); - end - jsLibVersions = struct('dojo', dojoVersion, 'react_js', reactVersion); - end % aboutJSLibs - - function fontColor(uiElement, newcolor) - % A method for manipulating text color. - newcolor = mlapptools.validateCSScolor(newcolor); - - [win, ID_struct] = mlapptools.getWebElements(uiElement); - - mlapptools.setStyle(win, 'color', newcolor, ID_struct); - end % fontColor - - function fontWeight(uiElement, weight) - % A method for manipulating font weight, which controls how thick or - % thin characters in text should be displayed. - weight = mlapptools.validateFontWeight(weight); - - [win, ID_struct] = mlapptools.getWebElements(uiElement); - - mlapptools.setStyle(win, 'font-weight', weight, ID_struct); - end % fontWeight - - function [childIDs] = getChildNodeIDs(win,ID_obj) - % A method for getting all children nodes (commonly
) of a specified node. - % Returns a vector WidgetID. - queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... - 'function(node){return node.childNodes;})[0];'],ID_obj.ID_attr, ID_obj.ID_val); - % The [0] above is required because an Array of Arrays is returned. - [~] = win.executeJS(queryStr); - % Try to establish an ID: - childIDs = mlapptools.establishIdentities(win); - % "Clear" the temporary JS variable - win.executeJS('W = undefined'); - end % getChildNodeIDs - - function [fullHTML] = getHTML(hFigOrWin) - % A method for dumping the HTML code of a uifigure. - % Intended for R2017b (and onward?) where the CEF url cannot be simply opened in - % an external browser. - % Mostly irrelevant for UIFigures as of the introduction of mlapptools.unlockUIFig(...), - % but can be useful for other non-uifigure webwindows. - %% Obtain webwindow handle: - if isa(hFigOrWin,'matlab.ui.Figure') - win = mlapptools.getWebWindow(hFigOrWin); - indepWW = false; - else - win = hFigOrWin; % rename the handle - %% Attempt to determine if this is an "independent" webwindow: - hF = mlapptools.figFromWebwindow(win); - if isempty(hF) - indepWW = true; - else - indepWW = false; - end - end - %% Get HTML according to webwindow type: - if indepWW - [~,hWB] = web(win.URL, '-new'); - fullHTML = hWB.getHtmlText(); - close(hWB); - %% TODO: Try to fix css paths: - % See: https://stackoverflow.com/q/50944935/ - %{ - % Get all elements: - win.executeJS('dojo.query("link")') - % Convert relative paths to absolute: - - % Replace paths in HTML: - - %} - else - % Get the outer html: - fullHTML = win.executeJS('document.documentElement.outerHTML'); - % Replace some strings for conversion to work well: - fullHTML = strrep(fullHTML,'%','%%'); - fullHTML = strrep(fullHTML,'><','>\n<'); - % Append the DOCTYPE header and remove quotes: - fullHTML = sprintf(['\n' fullHTML(2:end-1)]); - %% Optional things to do with the output: - % Display as web page: - %{ - web(['text://' fullHTML]); - %} - % Save as file: - %{ - fid = fopen('uifig_raw.html','w'); - fprintf(fid,'%s',fullHTML); - fclose(fid); - %} - end - end % getHTML - - function [parentID] = getParentNodeID(win,ID_obj) - % A method for getting the parent node (commonly
) of a specified node. - % Returns a scalar WidgetID. - queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... - 'function(node){return node.parentNode;});'],ID_obj.ID_attr, ID_obj.ID_val); - [~] = win.executeJS(queryStr); - % Try to establish an ID: - parentID = mlapptools.establishIdentities(win); - % "Clear" the temporary JS variable - win.executeJS('W = undefined'); - end % getParentNodeID + % Test if a *valid* webwindow already exists: + % (Written for compatibility with older releases) + exWW = matlab.internal.webwindowmanager.instance.findAllWebwindows(); + validWinID = find(~cellfun(@isempty,strfind({exWW.URL}.','uifigure')),... + 1, 'first'); %#ok + if isempty(validWinID) + f = uifigure; drawnow; tmpWindowCreated = true; + winID = numel(exWW) + 1; + else + tmpWindowCreated = false; + winID = validWinID; + end + + dojoVersion = matlab.internal.webwindowmanager.instance ... + .windowList(winID).executeJS('dojo.version'); + + reactVersion = matlab.internal.webwindowmanager.instance ... + .windowList(winID).executeJS(... + 'require("react/react.min").version;'); + + if tmpWindowCreated + delete(f); + end + + % If MATLAB is sufficiently new, convert the JSON to a struct: + if str2double(subsref(ver('matlab'), substruct('.','Version'))) >= 9.1 %R2016b + dv = jsondecode(dojoVersion); + dojoVersion = char(strrep(join(string(struct2cell(dv)),'.'),'..','.')); + reactVersion = jsondecode(reactVersion); + else + dojoVersion = strsplit(dojoVersion,{':',',','"','}','{'}); + dojoVersion = char(strjoin(dojoVersion([3,5,7,10]),'.')); + reactVersion = reactVersion(2:end-1); + end + jsLibVersions = struct('dojo', dojoVersion, 'react_js', reactVersion); + end % aboutJSLibs + + function fontColor(uiElement, newcolor) + % A method for manipulating text color. + newcolor = mlapptools.validateCSScolor(newcolor); + + [win, ID_struct] = mlapptools.getWebElements(uiElement); + + mlapptools.setStyle(win, 'color', newcolor, ID_struct); + end % fontColor + + function fontWeight(uiElement, weight) + % A method for manipulating font weight, which controls how thick or + % thin characters in text should be displayed. + weight = mlapptools.validateFontWeight(weight); + + [win, ID_struct] = mlapptools.getWebElements(uiElement); + + mlapptools.setStyle(win, 'font-weight', weight, ID_struct); + end % fontWeight + + function [childIDs] = getChildNodeIDs(win,ID_obj) + % A method for getting all children nodes (commonly
) of a specified node. + % Returns a vector WidgetID. + queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... + 'function(node){return node.childNodes;})[0];'],ID_obj.ID_attr, ID_obj.ID_val); + % The [0] above is required because an Array of Arrays is returned. + [~] = win.executeJS(queryStr); + % Try to establish an ID: + childIDs = mlapptools.establishIdentities(win); + % "Clear" the temporary JS variable + win.executeJS('W = undefined'); + end % getChildNodeIDs + + function [fullHTML] = getHTML(hFigOrWin) + % A method for dumping the HTML code of a uifigure. + % Intended for R2017b (and onward?) where the CEF url cannot be simply opened in + % an external browser. + % Mostly irrelevant for UIFigures as of the introduction of mlapptools.unlockUIFig(...), + % but can be useful for other non-uifigure webwindows. + %% Obtain webwindow handle: + if isa(hFigOrWin,'matlab.ui.Figure') + win = mlapptools.getWebWindow(hFigOrWin); + indepWW = false; + else + win = hFigOrWin; % rename the handle + %% Attempt to determine if this is an "independent" webwindow: + hF = mlapptools.figFromWebwindow(win); + if isempty(hF) + indepWW = true; + else + indepWW = false; + end + end + %% Get HTML according to webwindow type: + if indepWW + [~,hWB] = web(win.URL, '-new'); + fullHTML = hWB.getHtmlText(); + close(hWB); + %% TODO: Try to fix css paths: + % See: https://stackoverflow.com/q/50944935/ + %{ + % Get all elements: + win.executeJS('dojo.query("link")') + % Convert relative paths to absolute: - function [ID_obj] = getTableCellID(hTable, r, c) - % This method returns one or more ID objects, corresponding to specific cells in a - % uitable, as defined by the cells' row and column indices. - %% Constants: - TABLE_CLASS_NAME = 'matlab.ui.control.Table'; - CELL_ID_PREFIX = {'variableeditor_views_editors_UITableEditor_'}; - %% Input tests: - assert( isa(hTable, TABLE_CLASS_NAME) && ishandle(hTable), 'Invalid uitable handle!'); - nR = numel(r); nC = numel(c); - assert( nR == nC, 'The number of elements in r and c must be the same!'); - sz = size(hTable.Data); - assert( all(r <= sz(1)), 'One or more requested rows are out-of-bounds!'); - assert( all(c <= sz(2)), 'One or more requested columns are out-of-bounds!'); - %% Computing the offset - % If there's more than one uitable in the figure, IDs are assigned - % consecutively. For example, in the case of a 3x3 and 4x4 arrays, - % where the smaller table was created first, IDs will assigned as follows: - % - % [ 0 1 2 [ 9 10 11 12 - % 3 4 5 13 14 15 16 - % 6 7 8] 17 18 19 20 - % 21 22 23 24] - % - % ... which is why if want to change the 2nd table we need to offset the - % IDs by the total amount of elements in all preceding arrays, which is 9. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % Get siblings (flipping to negate that newer children appear first) - hC = flip(hTable.Parent.Children); - % Find which of them is a table: - hC = hC( arrayfun(@(x)isa(x, TABLE_CLASS_NAME), hC) ); - % Find the position of the current table in the list, and count the elements of - % all preceding tables: - offset = sum(arrayfun(@(x)numel(x.Data), hC(1:find(hC == hTable)-1))); - % Compute indices, r and c are reversed due to row-major ordering of the IDs - idx = sub2ind(sz, c, r) + offset - 1; % -1 because JS IDs are 0-based - idc = strcat(CELL_ID_PREFIX, num2str(idx(:))); - % Preallocation: - ID_obj(nR,1) = WidgetID; - % ID array population: - for indI = 1:numel(idx) - ID_obj(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, idc{indI} ); - end - end % getTableCellID - - function [win, widgetID] = getWebElements(uiElement) - % A method for obtaining the webwindow handle and the widget ID corresponding - % to the provided uifigure control. - % Get a handle to the webwindow - win = mlapptools.getWebWindow(uiElement); - mlapptools.waitTillWebwindowLoaded(win); - - % Find which element of the DOM we want to edit - switch uiElement.Type - case 'uitreenode' - p = uiElement.Parent; - if ~isa(p,'matlab.ui.container.Tree') - p.expand(); % The row must be visible to apply changes - end - warnState = mlapptools.toggleWarnings('off'); - widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); - warning(warnState); % Restore warning state - case {'uipanel','figure','uitabgroup','uitab'} - widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); - otherwise % default: - widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); - end - end % getWebElements - - function [win] = getWebWindow(hUIObj) - warnState = mlapptools.toggleWarnings('off'); - % Make sure we got a valid handle - % Check to make sure we're addressing the parent figure window, - % catches the case where the parent is a UIPanel or similar - hUIFig = ancestor(hUIObj, 'figure'); - - mlapptools.waitTillFigureLoaded(hUIFig); - % Since the above checks if a Controller exists, the below should work. - - hController = struct(struct(hUIFig).Controller); - % Check for Controller version: - switch subsref(ver('matlab'), substruct('.','Version')) - case {'9.0','9.1'} % R2016a or R2016b - win = hController.Container.CEF; - otherwise % R2017a onward - win = struct(hController.PlatformHost).CEF; - end + % Replace paths in HTML: - warning(warnState); % Restore warning state - - end % getWebWindow - - function [nfo] = getWidgetInfo(win, widgetID, verboseFlag) - % A method for gathering information about a specific dijit widget, if its - % HTML div id is known. - if ~strcmp(widgetID.ID_attr,'widgetid') - warning('getWidgetInfo:InappropriateIDAttribute',... - 'This method requires widgets identified by a ''widgetid'' attribute.'); - nfo = struct([]); - return - end - %% Handling required positional inputs: - assert(nargin >= 2,'mlapptools:getWidgetInfo:insufficientInputs',... - 'getWidgetInfo must be called with at least 2 inputs.'); - %% Handling optional inputs: - if nargin < 3 || isempty(verboseFlag) - verboseFlag = false; - end - %% Querying dijit - win.executeJS(['var W; require(["dijit/registry"], '... - 'function(registry){W = registry.byId("' widgetID.ID_val '");}); W = [W];']); - % Decoding - try - nfo = mlapptools.decodeDijitRegistryResult(win,verboseFlag); - catch ME - switch ME.identifier - case 'mlapptools:decodeDijitRegistryResult:noSuchWidget' - warning(ME.identifier, '%s', ME.message); - otherwise - warning('mlapptools:getWidgetInfo:unknownDecodingError',... - 'Decoding failed for an unexpected reason: %s', ME.message); - end - nfo = []; - end - % "Clear" the temporary JS variable - win.executeJS('W = undefined'); - end % getWidgetInfo - - function varargout = getWidgetList(hUIFig, verboseFlag) - % A method for listing all dijit widgets in a uifigure. - warnState = mlapptools.toggleWarnings('off'); - %% Handle missing inputs: - if nargin < 1 || isempty(hUIFig) || ~mlapptools.isUIFigure(hUIFig) - throw(MException('mlapptools:getWidgetList:noHandleProvided',... - 'Please provide a valid UIFigure handle as a first input.')); + %} + else + % Get the outer html: + fullHTML = win.executeJS('document.documentElement.outerHTML'); + % Replace some strings for conversion to work well: + fullHTML = strrep(fullHTML,'%','%%'); + fullHTML = strrep(fullHTML,'><','>\n<'); + % Append the DOCTYPE header and remove quotes: + fullHTML = sprintf(['\n' fullHTML(2:end-1)]); + %% Optional things to do with the output: + % Display as web page: + %{ + web(['text://' fullHTML]); + %} + % Save as file: + %{ + fid = fopen('uifig_raw.html','w'); + fprintf(fid,'%s',fullHTML); + fclose(fid); + %} + end + end % getHTML + + function [parentID] = getParentNodeID(win,ID_obj) + % A method for getting the parent node (commonly
) of a specified node. + % Returns a scalar WidgetID. + queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... + 'function(node){return node.parentNode;});'],ID_obj.ID_attr, ID_obj.ID_val); + [~] = win.executeJS(queryStr); + % Try to establish an ID: + parentID = mlapptools.establishIdentities(win); + % "Clear" the temporary JS variable + win.executeJS('W = undefined'); + end % getParentNodeID + + function [ID_obj] = getTableCellID(hTable, r, c) + % This method returns one or more ID objects, corresponding to specific cells in a + % uitable, as defined by the cells' row and column indices. + %% Constants: + TABLE_CLASS_NAME = 'matlab.ui.control.Table'; + CELL_ID_PREFIX = {'variableeditor_views_editors_UITableEditor_'}; + %% Input tests: + assert( isa(hTable, TABLE_CLASS_NAME) && ishandle(hTable), 'Invalid uitable handle!'); + nR = numel(r); nC = numel(c); + assert( nR == nC, 'The number of elements in r and c must be the same!'); + sz = size(hTable.Data); + assert( all(r <= sz(1)), 'One or more requested rows are out-of-bounds!'); + assert( all(c <= sz(2)), 'One or more requested columns are out-of-bounds!'); + %% Computing the offset + % If there's more than one uitable in the figure, IDs are assigned + % consecutively. For example, in the case of a 3x3 and 4x4 arrays, + % where the smaller table was created first, IDs will assigned as follows: + % + % [ 0 1 2 [ 9 10 11 12 + % 3 4 5 13 14 15 16 + % 6 7 8] 17 18 19 20 + % 21 22 23 24] + % + % ... which is why if want to change the 2nd table we need to offset the + % IDs by the total amount of elements in all preceding arrays, which is 9. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Get siblings (flipping to negate that newer children appear first) + hC = flip(hTable.Parent.Children); + % Find which of them is a table: + hC = hC( arrayfun(@(x)isa(x, TABLE_CLASS_NAME), hC) ); + % Find the position of the current table in the list, and count the elements of + % all preceding tables: + offset = sum(arrayfun(@(x)numel(x.Data), hC(1:find(hC == hTable)-1))); + % Compute indices, r and c are reversed due to row-major ordering of the IDs + idx = sub2ind(sz, c, r) + offset - 1; % -1 because JS IDs are 0-based + idc = strcat(CELL_ID_PREFIX, num2str(idx(:))); + % Preallocation: + ID_obj(nR,1) = WidgetID; + % ID array population: + for indI = 1:numel(idx) + ID_obj(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, idc{indI} ); + end + end % getTableCellID + + function [win, widgetID] = getWebElements(uiElement) + % A method for obtaining the webwindow handle and the widget ID corresponding + % to the provided uifigure control. + % Get a handle to the webwindow + win = mlapptools.getWebWindow(uiElement); + mlapptools.waitTillWebwindowLoaded(win); + + % Find which element of the DOM we want to edit + switch uiElement.Type + case 'uitreenode' + p = uiElement.Parent; + if ~isa(p,'matlab.ui.container.Tree') + p.expand(); % The row must be visible to apply changes end + warnState = mlapptools.toggleWarnings('off'); + widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); warning(warnState); % Restore warning state - if nargin < 2 || isempty(verboseFlag) - verboseFlag = false; - end - %% Process uifigure: - win = mlapptools.getWebWindow(hUIFig); - % Extract widgets from dijit registry: - win.executeJS(['var W; require(["dijit/registry"], '... - ' function(registry){W = registry.toArray();});']); - widgets = mlapptools.decodeDijitRegistryResult(win, verboseFlag); - % "Clear" the temporary JS variable - win.executeJS('W = undefined'); - %% Assign outputs: - varargout{1} = widgets; - if nargout == 2 - % Convert to a single table: - varargout{2} = struct2table(mlapptools.unifyStructs(widgets)); - end % getWidgetList - end - - function varargout = setStyle(varargin) - % A method providing an interface for modifying style attributes of uicontrols. - % - % WARNING: Due to the large amount of available style attributes and - % corresponding settings, input checking is not performed. As this - % might lead to unexpected results or errors - USE AT YOUR OWN RISK! - % - % "Overloads": - % 3-parameter call: - % widgetID = setStyle(hControl, styleAttr, styleValue) - % 4-parameter call: - % setStyle(hWin, styleAttr, styleValue, ID_obj) - - narginchk(3,4); - % Unpack inputs: - styleAttr = varargin{2}; - styleValue = varargin{3}; - - switch nargin - case 3 - hControl = varargin{1}; - % Get a handle to the webwindow - [win, ID_obj] = mlapptools.getWebElements(hControl); - case 4 - % By the time we have a WidgetID object, the webwindow handle is available - win = varargin{1}; - ID_obj = varargin{4}; - end - - % Handle the case of a non-scalar ID_obj recursively: - if ~isscalar(ID_obj) - arrayfun(@(x)mlapptools.setStyle(win, styleAttr, styleValue, x), ID_obj); - else - styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... - ID_obj.ID_attr, ID_obj.ID_val, styleAttr, styleValue); - % ^ this might result in junk if widgetId=='null'. - try - win.executeJS(styleSetStr); - % ^ this might crash in case of invalid styleAttr/styleValue. - catch ME - % Test for "Invalid or unexpected token": - ME = mlapptools.checkJavascriptSyntaxError(ME, styleSetStr); - rethrow(ME); - end - end - - % Assign outputs: - if nargout >= 1 - varargout{1} = ID_obj; - end - end % setStyle - - function setTimeout(hUIFig, newTimeoutInSec) - % Sets a custom timeout for dojo queries, specified in [s]. - setappdata(hUIFig, mlapptools.TAG_TIMEOUT, newTimeoutInSec); + case {'uipanel','figure','uitabgroup','uitab'} + widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); + otherwise % default: + widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); + end + end % getWebElements + + function [win] = getWebWindow(hUIObj) + warnState = mlapptools.toggleWarnings('off'); + % Make sure we got a valid handle + % Check to make sure we're addressing the parent figure window, + % catches the case where the parent is a UIPanel or similar + hUIFig = ancestor(hUIObj, 'figure'); + + mlapptools.waitTillFigureLoaded(hUIFig); + % Since the above checks if a Controller exists, the below should work. + + hController = struct(struct(hUIFig).Controller); + % Check for Controller version: + switch subsref(ver('matlab'), substruct('.','Version')) + case {'9.0','9.1'} % R2016a or R2016b + win = hController.Container.CEF; + otherwise % R2017a onward + win = struct(hController.PlatformHost).CEF; + end + + warning(warnState); % Restore warning state + + end % getWebWindow + + function [nfo] = getWidgetInfo(win, widgetID, verboseFlag) + % A method for gathering information about a specific dijit widget, if its + % HTML div id is known. + if ~strcmp(widgetID.ID_attr,'widgetid') + warning('getWidgetInfo:InappropriateIDAttribute',... + 'This method requires widgets identified by a ''widgetid'' attribute.'); + nfo = struct([]); + return + end + %% Handling required positional inputs: + assert(nargin >= 2,'mlapptools:getWidgetInfo:insufficientInputs',... + 'getWidgetInfo must be called with at least 2 inputs.'); + %% Handling optional inputs: + if nargin < 3 || isempty(verboseFlag) + verboseFlag = false; + end + %% Querying dijit + win.executeJS(['var W; require(["dijit/registry"], '... + 'function(registry){W = registry.byId("' widgetID.ID_val '");}); W = [W];']); + % Decoding + try + nfo = mlapptools.decodeDijitRegistryResult(win,verboseFlag); + catch ME + switch ME.identifier + case 'mlapptools:decodeDijitRegistryResult:noSuchWidget' + warning(ME.identifier, '%s', ME.message); + otherwise + warning('mlapptools:getWidgetInfo:unknownDecodingError',... + 'Decoding failed for an unexpected reason: %s', ME.message); end - - function textAlign(uiElement, alignment) - % A method for manipulating text alignment. - alignment = lower(alignment); - mlapptools.validateAlignmentStr(alignment) - - [win, ID_struct] = mlapptools.getWebElements(uiElement); - - mlapptools.setStyle(win, 'textAlign', alignment, ID_struct); - end % textAlign - - function unlockUIFig(hUIFig) - % This method allows the uifigure to be opened in an external browser, - % as was possible before R2017b. - if verLessThan('matlab','9.3') - % Do nothing, since this is not required pre-R2017b. - else - struct(hUIFig).Controller.ProxyView.PeerNode.setProperty('hostType','""'); - end - end % unlockUIFig - - function win = waitForFigureReady(hUIFig) - % This blocking method waits until a UIFigure and its widgets have fully loaded. - %% Make sure that the handle is valid: - assert(mlapptools.isUIFigure(hUIFig),... - 'mlapptools:getWebWindow:NotUIFigure',... - 'The provided window handle is not of a UIFigure.'); - assert(strcmp(hUIFig.Visible,'on'),... - 'mlapptools:getWebWindow:FigureNotVisible',... - 'Invisible figures are not supported.'); - %% Wait for the figure to appear: - mlapptools.waitTillFigureLoaded(hUIFig); - %% Make sure that Dojo is ready: - % Get a handle to the webwindow - win = mlapptools.getWebWindow(hUIFig); - mlapptools.waitTillWebwindowLoaded(win, hUIFig); - end % waitForFigureReady - - end % Public Static Methods - - methods (Static = true, Access = private) - - function ME = checkJavascriptSyntaxError(ME,styleSetStr) - if (strcmp(ME.identifier,'cefclient:webwindow:jserror')) - c = strfind(ME.message,'Uncaught SyntaxError:'); - if ~isempty(c) - v = str2double(regexp(ME.message(c:end),'-?\d+\.?\d*|-?\d*\.?\d+','match')); - msg = ['Syntax error: unexpected token in styleValue: ' styleSetStr(v(1),v(2))]; - causeException = MException('mlapptools:setStyle:invalidInputs',msg); - ME = addCause(ME,causeException); - end - end - end % checkJavascriptSyntaxError - - function widgets = decodeDijitRegistryResult(win, verboseFlag) - % As this method relies heavily on jsondecode, it is only supported on R >= 2016b - assert(strcmp('true', win.executeJS(... - 'this.hasOwnProperty("W") && W !== undefined && W instanceof Array && W.length > 0')),... - 'mlapptools:decodeDijitRegistryResult:noSuchWidget',... - 'The dijit registry doesn''t contain the specified widgetID.'); - - % Now that we know that W exists, let's try to decode it. - n = str2double(win.executeJS('W.length;')); - widgets = cell(n,1); - % Get the JSON representing the widget, then try to decode, while catching circular references - for ind1 = 1:n + nfo = []; + end + % "Clear" the temporary JS variable + win.executeJS('W = undefined'); + end % getWidgetInfo + + function varargout = getWidgetList(hUIFig, verboseFlag) + % A method for listing all dijit widgets in a uifigure. + warnState = mlapptools.toggleWarnings('off'); + %% Handle missing inputs: + if nargin < 1 || isempty(hUIFig) || ~mlapptools.isUIFigure(hUIFig) + throw(MException('mlapptools:getWidgetList:noHandleProvided',... + 'Please provide a valid UIFigure handle as a first input.')); + end + warning(warnState); % Restore warning state + if nargin < 2 || isempty(verboseFlag) + verboseFlag = false; + end + %% Process uifigure: + win = mlapptools.getWebWindow(hUIFig); + % Extract widgets from dijit registry: + win.executeJS(['var W; require(["dijit/registry"], '... + ' function(registry){W = registry.toArray();});']); + widgets = mlapptools.decodeDijitRegistryResult(win, verboseFlag); + % "Clear" the temporary JS variable + win.executeJS('W = undefined'); + %% Assign outputs: + varargout{1} = widgets; + if nargout == 2 + % Convert to a single table: + varargout{2} = struct2table(mlapptools.unifyStructs(widgets)); + end % getWidgetList + end + + function varargout = setStyle(varargin) + % A method providing an interface for modifying style attributes of uicontrols. + % + % WARNING: Due to the large amount of available style attributes and + % corresponding settings, input checking is not performed. As this + % might lead to unexpected results or errors - USE AT YOUR OWN RISK! + % + % "Overloads": + % 3-parameter call: + % widgetID = setStyle(hControl, styleAttr, styleValue) + % 4-parameter call: + % setStyle(hWin, styleAttr, styleValue, ID_obj) + + narginchk(3,4); + % Unpack inputs: + styleAttr = varargin{2}; + styleValue = varargin{3}; + + switch nargin + case 3 + hControl = varargin{1}; + % Get a handle to the webwindow + [win, ID_obj] = mlapptools.getWebElements(hControl); + case 4 + % By the time we have a WidgetID object, the webwindow handle is available + win = varargin{1}; + ID_obj = varargin{4}; + end + + % Handle the case of a non-scalar ID_obj recursively: + if ~isscalar(ID_obj) + arrayfun(@(x)mlapptools.setStyle(win, styleAttr, styleValue, x), ID_obj); + else + styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... + ID_obj.ID_attr, ID_obj.ID_val, styleAttr, styleValue); + % ^ this might result in junk if widgetId=='null'. + try + win.executeJS(styleSetStr); + % ^ this might crash in case of invalid styleAttr/styleValue. + catch ME + % Test for "Invalid or unexpected token": + ME = mlapptools.checkJavascriptSyntaxError(ME, styleSetStr); + rethrow(ME); + end + end + + % Assign outputs: + if nargout >= 1 + varargout{1} = ID_obj; + end + end % setStyle + + function setTimeout(hUIFig, newTimeoutInSec) + % Sets a custom timeout for dojo queries, specified in [s]. + setappdata(hUIFig, mlapptools.TAG_TIMEOUT, newTimeoutInSec); + end + + function textAlign(uiElement, alignment) + % A method for manipulating text alignment. + alignment = lower(alignment); + mlapptools.validateAlignmentStr(alignment) + + [win, ID_struct] = mlapptools.getWebElements(uiElement); + + mlapptools.setStyle(win, 'textAlign', alignment, ID_struct); + end % textAlign + + function unlockUIFig(hUIFig) + % This method allows the uifigure to be opened in an external browser, + % as was possible before R2017b. + if verLessThan('matlab','9.3') + % Do nothing, since this is not required pre-R2017b. + else + struct(hUIFig).Controller.ProxyView.PeerNode.setProperty('hostType','""'); + end + end % unlockUIFig + + function win = waitForFigureReady(hUIFig) + % This blocking method waits until a UIFigure and its widgets have fully loaded. + %% Make sure that the handle is valid: + assert(mlapptools.isUIFigure(hUIFig),... + 'mlapptools:getWebWindow:NotUIFigure',... + 'The provided window handle is not of a UIFigure.'); + assert(strcmp(hUIFig.Visible,'on'),... + 'mlapptools:getWebWindow:FigureNotVisible',... + 'Invisible figures are not supported.'); + %% Wait for the figure to appear: + mlapptools.waitTillFigureLoaded(hUIFig); + %% Make sure that Dojo is ready: + % Get a handle to the webwindow + win = mlapptools.getWebWindow(hUIFig); + mlapptools.waitTillWebwindowLoaded(win, hUIFig); + end % waitForFigureReady + + end % Public Static Methods + + methods (Static = true, Access = private) + + function ME = checkJavascriptSyntaxError(ME,styleSetStr) + if (strcmp(ME.identifier,'cefclient:webwindow:jserror')) + c = strfind(ME.message,'Uncaught SyntaxError:'); + if ~isempty(c) + v = str2double(regexp(ME.message(c:end),'-?\d+\.?\d*|-?\d*\.?\d+','match')); + msg = ['Syntax error: unexpected token in styleValue: ' styleSetStr(v(1),v(2))]; + causeException = MException('mlapptools:setStyle:invalidInputs',msg); + ME = addCause(ME,causeException); + end + end + end % checkJavascriptSyntaxError + + function widgets = decodeDijitRegistryResult(win, verboseFlag) + % As this method relies heavily on jsondecode, it is only supported on R >= 2016b + assert(strcmp('true', win.executeJS(... + 'this.hasOwnProperty("W") && W !== undefined && W instanceof Array && W.length > 0')),... + 'mlapptools:decodeDijitRegistryResult:noSuchWidget',... + 'The dijit registry doesn''t contain the specified widgetID.'); + + % Now that we know that W exists, let's try to decode it. + n = str2double(win.executeJS('W.length;')); + widgets = cell(n,1); + % Get the JSON representing the widget, then try to decode, while catching circular references + for ind1 = 1:n + try + widgets{ind1} = jsondecode(win.executeJS(sprintf('W[%d]', ind1-1))); + catch % handle circular references: + if verboseFlag + disp(['Node #' num2str(ind1-1) ' with id ' win.executeJS(sprintf('W[%d].id', ind1-1))... + ' could not be fully converted. Attempting fallback...']); + end + props = jsondecode(win.executeJS(sprintf('Object.keys(W[%d])', ind1-1))); + tmp = mlapptools.emptyStructWithFields(props); + validProps = fieldnames(tmp); + for indP = 1:numel(validProps) try - widgets{ind1} = jsondecode(win.executeJS(sprintf('W[%d]', ind1-1))); - catch % handle circular references: - if verboseFlag - disp(['Node #' num2str(ind1-1) ' with id ' win.executeJS(sprintf('W[%d].id', ind1-1))... - ' could not be fully converted. Attempting fallback...']); - end - props = jsondecode(win.executeJS(sprintf('Object.keys(W[%d])', ind1-1))); - tmp = mlapptools.emptyStructWithFields(props); - validProps = fieldnames(tmp); - for indP = 1:numel(validProps) - try - tmp.(validProps{indP}) = jsondecode(win.executeJS(sprintf(['W[%d].' props{indP}], ind1-1))); - catch - % Fallback could be executed recursively for all problematic field - % (to keep the most data), but for now do nothing. - end - end - widgets{ind1} = tmp; - clear props validProps tmp + tmp.(validProps{indP}) = jsondecode(win.executeJS(sprintf(['W[%d].' props{indP}], ind1-1))); + catch + % Fallback could be executed recursively for all problematic field + % (to keep the most data), but for now do nothing. end end - end % decodeDijitRegistryResult - - function eStruct = emptyStructWithFields(fields) - % A convenience method for creating an empty scalar struct with specific field - % names. - % INPUTS: - % fields - cell array of strings representing the required fieldnames. - - tmp = [ matlab.lang.makeValidName(fields(:)), cell(numel(fields),1)].'; - eStruct = struct(tmp{:}); - - end % emptyStructWithFields - - function [widgetID] = establishIdentities(win) % throws AssertionError - % A method for generating WidgetID objects from a list of DOM nodes. - assert(strcmp('true', win.executeJS([... - 'this.hasOwnProperty("W") && W !== undefined && ' ... - '(W instanceof NodeList || W instanceof Array) && W.length > 0'])),... - 'mlapptools:establishIdentities:noSuchNode',... - 'No nodes meet the condition.'); - - attrs = {'widgetid', 'id', 'data-tag', 'data-reactid'}; - nA = numel(attrs); - %% Preallocate output: - widgetID(win.executeJS('W.length') - '0', 1) = WidgetID; % "str2double" - %% - for indW = 1:numel(widgetID) - for indA = 1:nA - % Get the attribute value: - ID = win.executeJS(sprintf('W[%d].getAttribute("%s")', indW-1, attrs{indA})); - % Test result validity: - if ~strcmp(ID,'null') - % Create a WidgetID object and proceed to the next element in W: - widgetID(indW) = WidgetID(attrs{indA}, ID(2:end-1)); - break - end - if indA == nA - % Reaching this point means that the break didn't trigger for any of the attributes. - warning('The node''s ID could not be established using common attributes.'); - end - end - end - end % establishIdentity - - function [data_tag] = getDataTag(uiElement) - warnState = mlapptools.toggleWarnings('off'); - data_tag = char(struct(uiElement).Controller.ProxyView.PeerNode.getId); - warning(warnState); - end % getDataTag - - function hFig = figFromWebwindow(hWebwindow) - % Using this method is discouraged as it's relatively computation-intensive. - % Since the figure handle is not a property of the webwindow or its children - % (to our best knowledge), we must list all figures and check which of them - % is associated with the input webwindow. - hFigs = findall(groot, 'Type', 'figure'); - warnState = mlapptools.toggleWarnings('off'); - hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); - if isempty(hUIFigs) - hFig = gobjects(0); - return - end - hUIFigs = hUIFigs(strcmp({hUIFigs.Visible},'on')); % Hidden figures are ignored - ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); - warning(warnState); % Restore warning state - hFig = hFigs(hWebwindow == ww); - end % figFromWebwindow - - function [ID_obj] = getWidgetID(win, data_tag) - % This method returns a structure containing some uniquely-identifying information - % about a DOM node. - widgetquerystr = sprintf(... - 'dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); - try % should work for most UI objects - ID = win.executeJS(widgetquerystr); - ID_obj = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); - catch % fallback for problematic objects - warning('This widget is unsupported.'); -% ID_obj = mlapptools.getWidgetIDFromDijit(win, data_tag); - end - end % getWidgetID - - function ID_obj = getWidgetIDFromDijit(win, data_tag) - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EXPERIMENTAL METHOD!!! - win.executeJS(['var W; require(["dijit/registry"], '... - 'function(registry){W = registry.toArray().map(x => x.domNode.childNodes);});']); - nWidgets = jsondecode(win.executeJS('W.length')); - try - for ind1 = 0:nWidgets-1 - nChild = jsondecode(win.executeJS(sprintf('W[%d].length',ind1))); - for ind2 = 0:nChild-1 - tmp = win.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); - if isempty(tmp) - continue - else - tmp = jsondecode(tmp); - end - if isfield(tmp,'tag') && strcmp(tmp.tag,data_tag) - ID = win.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); - error('Bailout!'); - end - end - end - ID_obj = WidgetID('',''); - catch - % Fix for the case of top-level tree nodes: - switch tmp.type - case 'matlab.ui.container.TreeNode' - tmp = jsondecode(win.executeJS(sprintf(... - 'dojo.byId(%s).childNodes[0].childNodes[0].childNodes[0].childNodes[%d].dataset',... - ID(2:end-1),ind2-1))); - ID_obj = WidgetID('data-reactid', tmp.reactid); - end - end - end % getWidgetIDFromDijit - - function to = getTimeout(hFig) - to = getappdata(hFig, mlapptools.TAG_TIMEOUT); - if isempty(to), to = mlapptools.QUERY_TIMEOUT; end - end % getTimeout - - function tf = isUIFigure(hList) - tf = arrayfun(@(x)isa(x,'matlab.ui.Figure') && ... - isstruct(struct(x).ControllerInfo), hList); - end % isUIFigure - - function oldState = toggleWarnings(togglestr) - OJF = 'MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame'; - SOO = 'MATLAB:structOnObject'; - if nargout > 0 - oldState = [warning('query',OJF); warning('query',SOO)]; - end - switch lower(togglestr) - case 'on' - warning('on',OJF); - warning('on',SOO); - case 'off' - warning('off',OJF); - warning('off',SOO); - otherwise - % Do nothing - end - end % toggleWarnings - - function uStruct = unifyStructs(cellOfStructs) - % A method for merging structs having *some* overlapping field names. - - fields = cellfun(@fieldnames, cellOfStructs, 'UniformOutput', false); - uFields = unique(vertcat(fields{:})); - sz = numel(cellOfStructs); - uStruct = repmat(mlapptools.emptyStructWithFields(uFields),sz,1); - for ind1 = 1:sz - fields = fieldnames(cellOfStructs{ind1}); - for ind2 = 1:numel(fields) - uStruct(ind1).(fields{ind2}) = cellOfStructs{ind1}.(fields{ind2}); - end - end - end % unifyStructs - - function validateAlignmentStr(alignment) - if ~ischar(alignment) - msgID = 'mlapptools:alignstring:InvalidInputIype'; - error(msgID, 'Expected ''%s'', inputs of type ''%s'' not supported', ... - class('Dev-il'), class(alignment)); - end - - validstr = {'left', 'right', 'center', 'justify', 'initial'}; - if ~any(ismember(validstr, alignment)) - msgID = 'mlapptools:alignstring:InvalidAlignmentString'; - error(msgID, 'Invalid string alignment specified: ''%s''', alignment); - end - end % validateAlignmentStr - - function [newcolor] = validateCSScolor(newcolor) - % TODO - end % validateCSScolor - - function [weight] = validateFontWeight(weight) - if ischar(weight) - weight = lower(weight); - validstrs = {'normal', 'bold', 'bolder', 'lighter', 'initial'}; - - if ~any(ismember(weight, validstrs)) - msgID = 'mlapptools:fontWeight:InvalidFontWeightString'; - error(msgID, 'Invalid font weight specified: ''%s''', weight); - end - elseif isnumeric(weight) - weight = round(weight, -2); - if weight < 100 - weight = 100; - elseif weight > 900 - weight = 900; - end - - weight = num2str(weight); + widgets{ind1} = tmp; + clear props validProps tmp + end + end + end % decodeDijitRegistryResult + + function eStruct = emptyStructWithFields(fields) + % A convenience method for creating an empty scalar struct with specific field + % names. + % INPUTS: + % fields - cell array of strings representing the required fieldnames. + + tmp = [ matlab.lang.makeValidName(fields(:)), cell(numel(fields),1)].'; + eStruct = struct(tmp{:}); + + end % emptyStructWithFields + + function [widgetID] = establishIdentities(win) % throws AssertionError + % A method for generating WidgetID objects from a list of DOM nodes. + assert(strcmp('true', win.executeJS([... + 'this.hasOwnProperty("W") && W !== undefined && ' ... + '(W instanceof NodeList || W instanceof Array) && W.length > 0'])),... + 'mlapptools:establishIdentities:noSuchNode',... + 'No nodes meet the condition.'); + + attrs = {'widgetid', 'id', 'data-tag', 'data-reactid'}; + nA = numel(attrs); + %% Preallocate output: + widgetID(win.executeJS('W.length') - '0', 1) = WidgetID; % "str2double" + %% + for indW = 1:numel(widgetID) + for indA = 1:nA + % Get the attribute value: + ID = win.executeJS(sprintf('W[%d].getAttribute("%s")', indW-1, attrs{indA})); + % Test result validity: + if ~strcmp(ID,'null') + % Create a WidgetID object and proceed to the next element in W: + widgetID(indW) = WidgetID(attrs{indA}, ID(2:end-1)); + break + end + if indA == nA + % Reaching this point means that the break didn't trigger for any of the attributes. + warning('The node''s ID could not be established using common attributes.'); + end + end + end + end % establishIdentity + + function [data_tag] = getDataTag(uiElement) + warnState = mlapptools.toggleWarnings('off'); + data_tag = char(struct(uiElement).Controller.ProxyView.PeerNode.getId); + warning(warnState); + end % getDataTag + + function hFig = figFromWebwindow(hWebwindow) + % Using this method is discouraged as it's relatively computation-intensive. + % Since the figure handle is not a property of the webwindow or its children + % (to our best knowledge), we must list all figures and check which of them + % is associated with the input webwindow. + hFigs = findall(groot, 'Type', 'figure'); + warnState = mlapptools.toggleWarnings('off'); + hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); + if isempty(hUIFigs) + hFig = gobjects(0); + return + end + hUIFigs = hUIFigs(strcmp({hUIFigs.Visible},'on')); % Hidden figures are ignored + ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); + warning(warnState); % Restore warning state + hFig = hFigs(hWebwindow == ww); + end % figFromWebwindow + + function [ID_obj] = getWidgetID(win, data_tag) + % This method returns a structure containing some uniquely-identifying information + % about a DOM node. + widgetquerystr = sprintf(... + 'dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); + try % should work for most UI objects + ID = win.executeJS(widgetquerystr); + ID_obj = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); + catch % fallback for problematic objects + warning('This widget is unsupported.'); + % ID_obj = mlapptools.getWidgetIDFromDijit(win, data_tag); + end + end % getWidgetID + + function ID_obj = getWidgetIDFromDijit(win, data_tag) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EXPERIMENTAL METHOD!!! + win.executeJS(['var W; require(["dijit/registry"], '... + 'function(registry){W = registry.toArray().map(x => x.domNode.childNodes);});']); + nWidgets = jsondecode(win.executeJS('W.length')); + try + for ind1 = 0:nWidgets-1 + nChild = jsondecode(win.executeJS(sprintf('W[%d].length',ind1))); + for ind2 = 0:nChild-1 + tmp = win.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); + if isempty(tmp) + continue else - msgID = 'mlapptools:fontWeight:InvalidFontWeight'; - error(msgID, 'Invalid font weight specified: ''%s''', weight); - end - end % validateFontWeight - - function waitTillFigureLoaded(hFig) - % A blocking method that ensures a UIFigure has fully loaded. - warnState = mlapptools.toggleWarnings('off'); - to = mlapptools.getTimeout(hFig); - tic - while (toc < to) && isempty(struct(hFig).Controller) - pause(0.01) + tmp = jsondecode(tmp); end - if toc > to - msgID = 'mlapptools:waitTillFigureLoaded:TimeoutReached'; - error(msgID, ... - ['Waiting for the figure to load has timed out after %u seconds. ' ... - 'Try increasing the timeout. If the figure clearly loaded in time, yet '... - 'this error remains - it might be a bug in the tool! ' ... - 'Please let the developers know through GitHub.'], ... - to); + if isfield(tmp,'tag') && strcmp(tmp.tag,data_tag) + ID = win.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); + error('Bailout!'); end - warning(warnState); - end % waitTillFigureLoaded - - function waitTillWebwindowLoaded(hWebwindow, hFig) - % A blocking method that ensures a certain webwindow has fully loaded. - if nargin < 2 - hFig = mlapptools.figFromWebwindow(hWebwindow); - end - - to = mlapptools.getTimeout(hFig); - tic - while (toc < to) && ~jsondecode(hWebwindow.executeJS(... - 'this.hasOwnProperty("require") && require !== undefined && typeof(require) === "function"')) - pause(0.01) - end - if toc > to - msgID = 'mlapptools:waitTillWebwindowLoaded:TimeoutReached'; - error(msgID, ... - ['Waiting for the webwindow to load has timed out after %u seconds. ' ... - 'Try increasing the timeout. If the figure clearly loaded in time, yet '... - 'this error remains - it might be a bug in the tool! ' ... - 'Please let the developers know through GitHub.'], ... - to); - else - hWebwindow.executeJS('require(["dojo/ready"], function(ready){});'); - end - end % waitTillWebwindowLoaded + end + end + ID_obj = WidgetID('',''); + catch + % Fix for the case of top-level tree nodes: + switch tmp.type + case 'matlab.ui.container.TreeNode' + tmp = jsondecode(win.executeJS(sprintf(... + 'dojo.byId(%s).childNodes[0].childNodes[0].childNodes[0].childNodes[%d].dataset',... + ID(2:end-1),ind2-1))); + ID_obj = WidgetID('data-reactid', tmp.reactid); + end + end + end % getWidgetIDFromDijit + + function to = getTimeout(hFig) + to = getappdata(hFig, mlapptools.TAG_TIMEOUT); + if isempty(to), to = mlapptools.QUERY_TIMEOUT; end + end % getTimeout + + function tf = isUIFigure(hList) + tf = arrayfun(@(x)isa(x,'matlab.ui.Figure') && ... + isstruct(struct(x).ControllerInfo), hList); + end % isUIFigure + + function oldState = toggleWarnings(togglestr) + OJF = 'MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame'; + SOO = 'MATLAB:structOnObject'; + if nargout > 0 + oldState = [warning('query',OJF); warning('query',SOO)]; + end + switch lower(togglestr) + case 'on' + warning('on',OJF); + warning('on',SOO); + case 'off' + warning('off',OJF); + warning('off',SOO); + otherwise + % Do nothing + end + end % toggleWarnings + + function uStruct = unifyStructs(cellOfStructs) + % A method for merging structs having *some* overlapping field names. + + fields = cellfun(@fieldnames, cellOfStructs, 'UniformOutput', false); + uFields = unique(vertcat(fields{:})); + sz = numel(cellOfStructs); + uStruct = repmat(mlapptools.emptyStructWithFields(uFields),sz,1); + for ind1 = 1:sz + fields = fieldnames(cellOfStructs{ind1}); + for ind2 = 1:numel(fields) + uStruct(ind1).(fields{ind2}) = cellOfStructs{ind1}.(fields{ind2}); + end + end + end % unifyStructs + + function validateAlignmentStr(alignment) + if ~ischar(alignment) + msgID = 'mlapptools:alignstring:InvalidInputIype'; + error(msgID, 'Expected ''%s'', inputs of type ''%s'' not supported', ... + class('Dev-il'), class(alignment)); + end + + validstr = {'left', 'right', 'center', 'justify', 'initial'}; + if ~any(ismember(validstr, alignment)) + msgID = 'mlapptools:alignstring:InvalidAlignmentString'; + error(msgID, 'Invalid string alignment specified: ''%s''', alignment); + end + end % validateAlignmentStr + + function [newcolor] = validateCSScolor(newcolor) + % TODO + end % validateCSScolor + + function [weight] = validateFontWeight(weight) + if ischar(weight) + weight = lower(weight); + validstrs = {'normal', 'bold', 'bolder', 'lighter', 'initial'}; - function htmlRootPath = getFullPathFromWW(win) - % Get a local href value, e.g. from an included main.css - href = win.executeJS('document.body.getElementsByTagName("link")[0].href'); - htmlRootPath = win.executeJS(['var link = document.createElement("a"); link.href = ' href ';']); + if ~any(ismember(weight, validstrs)) + msgID = 'mlapptools:fontWeight:InvalidFontWeightString'; + error(msgID, 'Invalid font weight specified: ''%s''', weight); + end + elseif isnumeric(weight) + weight = round(weight, -2); + if weight < 100 + weight = 100; + elseif weight > 900 + weight = 900; end - - end % Private Static Methods + + weight = num2str(weight); + else + msgID = 'mlapptools:fontWeight:InvalidFontWeight'; + error(msgID, 'Invalid font weight specified: ''%s''', weight); + end + end % validateFontWeight + + function waitTillFigureLoaded(hFig) + % A blocking method that ensures a UIFigure has fully loaded. + warnState = mlapptools.toggleWarnings('off'); + to = mlapptools.getTimeout(hFig); + tic + while (toc < to) && isempty(struct(hFig).Controller) + pause(0.01) + end + if toc > to + msgID = 'mlapptools:waitTillFigureLoaded:TimeoutReached'; + error(msgID, ... + ['Waiting for the figure to load has timed out after %u seconds. ' ... + 'Try increasing the timeout. If the figure clearly loaded in time, yet '... + 'this error remains - it might be a bug in the tool! ' ... + 'Please let the developers know through GitHub.'], ... + to); + end + warning(warnState); + end % waitTillFigureLoaded + + function waitTillWebwindowLoaded(hWebwindow, hFig) + % A blocking method that ensures a certain webwindow has fully loaded. + if nargin < 2 + hFig = mlapptools.figFromWebwindow(hWebwindow); + end + + to = mlapptools.getTimeout(hFig); + tic + while (toc < to) && ~jsondecode(hWebwindow.executeJS(... + 'this.hasOwnProperty("require") && require !== undefined && typeof(require) === "function"')) + pause(0.01) + end + if toc > to + msgID = 'mlapptools:waitTillWebwindowLoaded:TimeoutReached'; + error(msgID, ... + ['Waiting for the webwindow to load has timed out after %u seconds. ' ... + 'Try increasing the timeout. If the figure clearly loaded in time, yet '... + 'this error remains - it might be a bug in the tool! ' ... + 'Please let the developers know through GitHub.'], ... + to); + else + hWebwindow.executeJS('require(["dojo/ready"], function(ready){});'); + end + end % waitTillWebwindowLoaded + + function htmlRootPath = getFullPathFromWW(win) + % Get a local href value, e.g. from an included main.css + href = win.executeJS('document.body.getElementsByTagName("link")[0].href'); + htmlRootPath = win.executeJS(['var link = document.createElement("a"); link.href = ' href ';']); + end + end % Private Static Methods + end % classdef %{ From 4dfe18a638dc9635bc20614480e60b4fa7043048 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 7 Aug 2018 15:31:27 +0300 Subject: [PATCH 05/18] Minor refactoring in fontColor --- mlapptools.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 8a5a437..cd3f27f 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -70,13 +70,13 @@ jsLibVersions = struct('dojo', dojoVersion, 'react_js', reactVersion); end % aboutJSLibs - function fontColor(uiElement, newcolor) + function fontColor(uiElement, color) % A method for manipulating text color. - newcolor = mlapptools.validateCSScolor(newcolor); + color = mlapptools.validateCSScolor(color); [win, ID_struct] = mlapptools.getWebElements(uiElement); - mlapptools.setStyle(win, 'color', newcolor, ID_struct); + mlapptools.setStyle(win, 'color', color, ID_struct); end % fontColor function fontWeight(uiElement, weight) From afd8065c92715b77b6f01e98b1beb5e23e4f68fe Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 7 Aug 2018 17:36:30 +0300 Subject: [PATCH 06/18] Added the `addClasses` and `addCssToHead` methods - The new methods are intended for adding CSS classes to HTML nodes. - README.md updated accordingly. --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- mlapptools.m | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c2fa53..37936a2 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ published Wednesday, September 7th, 2016. ## Methods [`aboutJSLibs`](#aboutJSLibs) - Return the loaded versions of Dojo and React. +[`addClasses`](#addClasses) - Add specified CSS classes to one or more DOM nodes. +[`addCssToHead`](#addCssToHead) - Inject inline CSS into the section of the figure's HTML. [`fontColor`](#fontColor) - Modify font color. [`fontWeight`](#fontWeight) - Modify font weight. [`getChildNodeIDs`](#getChildNodeIDs) - Get all children DOM nodes of a specified node. @@ -59,14 +61,59 @@ ans = react_js: '0.14.7' ``` +----------------- + + +#### *mlapptools*.**addClasses**(*uiElement*, *cssClasses*) + +##### Description + +Adds the specified CSS class (if `char` vector) or classes (if `cell` array of `char` vectors) to a UI element. + +##### Examples + +Using the demo GUI generated by `./Demo/DOMdemoGUI.m` + +```MATLAB +%% 1. Create a figure: +hFig = DOMdemoGUI; + +%% 2. Get a handle to the webwindow and the TextArea: +[hWin, widgetID] = mlapptools.getWebElements(hFig.TextArea); + +%% 3. Define inline CSS and add it to the figure HTML: +cssText = ['""']; +mlapptools.addCssToHead(hWin, cssText); + +%% 4. Add animation to control: +mlapptools.setStyle(hWin, '-webkit-animation', 'mymove 5s infinite', widgetID); +``` + +Another example is available in the blog post "[Customizing web-GUI uipanel](https://undocumentedmatlab.com/blog/customizing-web-gui-uipanel)" by Khris Griffis. + +----------------- + + +#### *mlapptools*.**addCssToHead**(*hWin*, *cssText*) + +##### Description + +A method for adding inline CSS to the HTML `` section. +See also: [`stringify.m`](https://github.com/Khlick/matlabUiHacks/blob/master/utils/stringify.m). + +##### Examples +See example for [`addClasses`](#addClasses). + ----------------- -#### *mlapptools*.**fontColor**(*uiElement*, *newcolor*) +#### *mlapptools*.**fontColor**(*uiElement*, *color*) ##### Description -Set the font color of the specified UI element, `'uiElement'`, to the input color, `newcolor`. `newcolor` can be a +Set the font color of the specified UI element, `'uiElement'`, to the input color, `color`. `color` can be a predefined color string or a string containing a valid CSS color method call. Valid color specifications are: diff --git a/mlapptools.m b/mlapptools.m index cd3f27f..b171383 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -6,6 +6,8 @@ % MLAPPTOOLS' public methods: % % aboutJSLibs - Return version information about certain JS libraries. + % addClasses - Add specified CSS classes to one or more DOM nodes. + % addCssToHead - Inject inline CSS into the section of the figure's HTML. % fontColor - Modify font color. % fontWeight - Modify font weight. % getHTML - Return the full HTML code of a uifigure. @@ -70,6 +72,47 @@ jsLibVersions = struct('dojo', dojoVersion, 'react_js', reactVersion); end % aboutJSLibs + function addClasses(uiElement, cssClasses) + % A method for adding CSS classes to DOM nodes. + + % Ensure we end up with a space-delimited list of classes: + if ~ischar(cssClasses) && numel(cssClasses) > 1 + classList = strjoin(cssClasses, ' '); + else + classList = cssClasses; + end + + if ~isscalar(uiElement) + arrayfun(@(x)mlapptools.addClasses(x, cssClasses), uiElement); + else + [win, ID_struct] = mlapptools.getWebElements(uiElement); + % Construct a dojo.js statement: + classSetStr = sprintf(... + 'dojo.addClass(dojo.query("[%s = ''%s'']")[0], "%s")',... + ID_struct.ID_attr, ID_struct.ID_val, classList); + + % Add the class(es) to the DOM node: + win.executeJS(classSetStr); + end + + end % addClasses + + function addCssToHead(hWin, cssText) + % A method for adding an inline CSS to the HTML section. + + % in the future, cssText could be a local or a remote ile path: + if isfile(cssText) + warning(['.css files are not supported at this time. Please provide a "stringified" CSS instead.'... + '\nSee also: https://github.com/Khlick/matlabUiHacks/blob/master/utils/stringify.m'],[]); + elseif ~isempty(regexpi(cssText,'http(s)?://')) + warning(['Remote .css files are not supported at this time. Please provide a "stringified" CSS instead.'... + '\nSee also: https://github.com/Khlick/matlabUiHacks/blob/master/utils/stringify.m'],[]); + end + + % Inject the CSS: + hWin.executeJS(['document.head.innerHTML += ', cssText]); + end % addCssToHead + function fontColor(uiElement, color) % A method for manipulating text color. color = mlapptools.validateCSScolor(color); From 8daf390404ec6b70bf46a21bf088e571eaa57ce6 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 7 Aug 2018 17:37:20 +0300 Subject: [PATCH 07/18] Fixed certificate checking logic to avoid an uninitialized variable. --- mlapptools.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index b171383..13426a5 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -456,13 +456,13 @@ function unlockUIFig(hUIFig) % - In Chromium, visit chrome://settings/certificates, click Authorities, % then click import, and select your .pem. % - In Firefox, visit about:preferences#privacy, click Certificates, - % View Certificates, Authorities, then click Import and select your .pem. + % View Certificates, Authorities, then click Import and select your .pem. SUCCESS_CODE = 0; CL = connector.getCertificateLocation(); % certificate location; if isempty(CL), CL = fullfile(prefdir, 'thisMatlab.pem'); end %% Test if certificate is already accepted: switch true - case ispc + case ispc [s,c] = system('certutil -verifystore -user "Root" localhost'); case isunix [s,c] = system(['openssl crl2pkcs7 -nocrl -certfile '... @@ -475,7 +475,9 @@ function unlockUIFig(hUIFig) isAccepted = s == SUCCESS_CODE; %% Try to import certificate: - if ~isAccepted + if isAccepted + wasImported = false; + else reply = questdlg('Certificate not found. Would you like to import it?',... 'Import "localhost" certificate','Yes','No','Yes'); if strcmp(reply,'Yes'), switch true %#ok @@ -518,7 +520,7 @@ function unlockUIFig(hUIFig) ' > Import, and select your .pem.'],... ['- In Firefox, visit about:preferences#privacy, click Certificates > ',... 'View Certificates > Authorities > Import, and select your .pem.'],... - ['The certificate is found here: ' CL ]); + ['The certificate is found here: ' CL ]); end end % checkCert end % unlockUIFig From 162af02e3781c5e7ee9cf824cf7d5ffe0932f23f Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 12 Aug 2018 19:52:00 +0300 Subject: [PATCH 08/18] Getting the widgetID of a uitable is now possible. ..although it isn't terribly useful. The main usefulness is likely for traversing the DOM (getting to parents / children of this id). Perhaps in the future we'll return a more useful WidgetID for uitables. --- mlapptools.m | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mlapptools.m b/mlapptools.m index 13426a5..4e4e5f0 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -277,6 +277,16 @@ function fontWeight(uiElement, weight) warning(warnState); % Restore warning state case {'uipanel','figure','uitabgroup','uitab'} widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); + case 'uitable' + % uitables are inconsistent with other elements, their id always starts with + % "mgg_". So we list all widgets and select the "table-suspects" among them. + % Note: the listing is not really necessary, as it is possible to search for + % nodes having a property that starts with a certain string: E[foo^="bar"] + % web(['http://dojotoolkit.org/reference-guide/1.10/dojo/query.html',... + % '#additional-selectors-supported-by-lite-engine'], '-browser'); + [~,tmp] = mlapptools.getWidgetList( ancestor(uiElement,'figure') ); + widgetID = arrayfun(@(x)WidgetID('id',x), ... + string(tmp.id(contains(tmp.id, "mgg_")))); otherwise % default: widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); end From e426025db3431fd37f61d865ef724a49ace38748 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 12 Aug 2018 20:22:49 +0300 Subject: [PATCH 09/18] The IDs returned by getTableCellID now correspond to `mwTextField` objs This means that the style loss on resize is no longer an issue. + Small fix for the previous commit (coding style). TODO: add `getTableCellID` to API documentation. --- mlapptools.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 4e4e5f0..e95871d 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -252,9 +252,13 @@ function fontWeight(uiElement, weight) idc = strcat(CELL_ID_PREFIX, num2str(idx(:))); % Preallocation: ID_obj(nR,1) = WidgetID; + % Get the window handle so we could execute some JS commands: + hWin = mlapptools.getWebWindow(hTable); % ID array population: for indI = 1:numel(idx) - ID_obj(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, idc{indI} ); + jsCommand = sprintf('dojo.byId(%s).childNodes[0].id', idc{indI}); + textFieldID = hWin.executeJS(jsCommand); + ID_obj(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, textFieldID(2:end-1) ); end end % getTableCellID @@ -278,6 +282,7 @@ function fontWeight(uiElement, weight) case {'uipanel','figure','uitabgroup','uitab'} widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); case 'uitable' + TAB_PREFIX = "mgg_"; % uitables are inconsistent with other elements, their id always starts with % "mgg_". So we list all widgets and select the "table-suspects" among them. % Note: the listing is not really necessary, as it is possible to search for @@ -285,8 +290,8 @@ function fontWeight(uiElement, weight) % web(['http://dojotoolkit.org/reference-guide/1.10/dojo/query.html',... % '#additional-selectors-supported-by-lite-engine'], '-browser'); [~,tmp] = mlapptools.getWidgetList( ancestor(uiElement,'figure') ); - widgetID = arrayfun(@(x)WidgetID('id',x), ... - string(tmp.id(contains(tmp.id, "mgg_")))); + widgetID = arrayfun(@(x)WidgetID(mlapptools.DEF_ID_ATTRIBUTE, x), ... + string(tmp.id(contains(tmp.id, TAB_PREFIX)))); otherwise % default: widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); end From 055ea1ef7de2134a05364fa93dfa1e50b79141f5 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Fri, 17 Aug 2018 18:23:54 +0300 Subject: [PATCH 10/18] Renamed "addCssToHead" to "addToHead" - Updated README accordingly. - Minor other README updates. --- README.md | 16 ++++++++-------- mlapptools.m | 32 ++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 37936a2..859ad40 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ published Wednesday, September 7th, 2016. [`aboutJSLibs`](#aboutJSLibs) - Return the loaded versions of Dojo and React. [`addClasses`](#addClasses) - Add specified CSS classes to one or more DOM nodes. -[`addCssToHead`](#addCssToHead) - Inject inline CSS into the section of the figure's HTML. +[`addToHead`](#addToHead) - Inject inline CSS or JS into the `` section of the figure's HTML. [`fontColor`](#fontColor) - Modify font color. [`fontWeight`](#fontWeight) - Modify font weight. [`getChildNodeIDs`](#getChildNodeIDs) - Get all children DOM nodes of a specified node. @@ -75,28 +75,28 @@ Adds the specified CSS class (if `char` vector) or classes (if `cell` array of ` Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB -%% 1. Create a figure: +% Create a figure: hFig = DOMdemoGUI; -%% 2. Get a handle to the webwindow and the TextArea: +% Get a handle to the webwindow and the TextArea: [hWin, widgetID] = mlapptools.getWebElements(hFig.TextArea); -%% 3. Define inline CSS and add it to the figure HTML: +% Define inline CSS animation and add it to the figure HTML: cssText = ['""']; -mlapptools.addCssToHead(hWin, cssText); +mlapptools.addToHead(hWin, cssText); -%% 4. Add animation to control: +% Activate animation on control: mlapptools.setStyle(hWin, '-webkit-animation', 'mymove 5s infinite', widgetID); ``` Another example is available in the blog post "[Customizing web-GUI uipanel](https://undocumentedmatlab.com/blog/customizing-web-gui-uipanel)" by Khris Griffis. ----------------- - + -#### *mlapptools*.**addCssToHead**(*hWin*, *cssText*) +#### *mlapptools*.**addToHead**(*hWin*, *cssText*) ##### Description diff --git a/mlapptools.m b/mlapptools.m index e95871d..da3c4ba 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -97,21 +97,23 @@ function addClasses(uiElement, cssClasses) end % addClasses - function addCssToHead(hWin, cssText) - % A method for adding an inline CSS to the HTML section. + function addToHead(hWin, nodeText) + % A method for adding nodes (" or "".'],[]); + elseif ~isempty(regexpi(nodeText,'http(s)?://')) + warning(['Remote files are not supported at this time.',... + ' Please provide a "stringified" input instead.'... + '\nInput can be "" or "".'],[]); end - % Inject the CSS: - hWin.executeJS(['document.head.innerHTML += ', cssText]); - end % addCssToHead + % Inject the nodeText: + hWin.executeJS(['document.head.innerHTML += ', nodeText]); + end % addToHead function fontColor(uiElement, color) % A method for manipulating text color. @@ -653,7 +655,7 @@ function unlockUIFig(hUIFig) function [data_tag] = getDataTag(uiElement) warnState = mlapptools.toggleWarnings('off'); - data_tag = char(struct(uiElement).Controller.ProxyView.PeerNode.getId); + data_tag = char( struct(uiElement).Controller.ProxyView.PeerNode.getId() ); warning(warnState); end % getDataTag @@ -664,7 +666,9 @@ function unlockUIFig(hUIFig) % is associated with the input webwindow. hFigs = findall(groot, 'Type', 'figure'); warnState = mlapptools.toggleWarnings('off'); - hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); + % Distinguish java figures from web figures: + hUIFigs = hFigs(arrayfun(@(x)isstruct(struct(x).ControllerInfo), hFigs)); + % hUIFigs = hFigs(arrayfun(@matlab.ui.internal.isUIFigure, hFigs)); % "official" way? if isempty(hUIFigs) hFig = gobjects(0); return From 8c13979bdcd8a8695f3f42044ed1778d417d028d Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Fri, 17 Aug 2018 19:46:03 +0300 Subject: [PATCH 11/18] Consistent input and output argument naming Updated README accordingly. --- README.md | 54 +++++------ mlapptools.m | 263 ++++++++++++++++++++++++++------------------------- 2 files changed, 161 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index 859ad40..f0e19e1 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ ans = ----------------- -#### *mlapptools*.**addClasses**(*uiElement*, *cssClasses*) +#### *mlapptools*.**addClasses**(*hUIElement*, *cssClasses*) ##### Description @@ -109,11 +109,11 @@ See example for [`addClasses`](#addClasses). ----------------- -#### *mlapptools*.**fontColor**(*uiElement*, *color*) +#### *mlapptools*.**fontColor**(*hUIElement*, *color*) ##### Description -Set the font color of the specified UI element, `'uiElement'`, to the input color, `color`. `color` can be a +Set the font color of the specified UI element, `'hUIElement'`, to the input color, `color`. `color` can be a predefined color string or a string containing a valid CSS color method call. Valid color specifications are: @@ -143,11 +143,11 @@ mlapptools.fontColor(myGUI.TextArea, 'rgb(255,165,0)'); ----------------- -#### *mlapptools*.**fontWeight**(*uiElement*, *weight*) +#### *mlapptools*.**fontWeight**(*hUIElement*, *weight*) ##### Description -Set the font weight of the specified UI element, `uiElement`, to the input weight string or integer, `weight`. +Set the font weight of the specified UI element, `hUIElement`, to the input weight string or integer, `weight`. For this setting to have an effect, the font being used must have built-in faces that match the specified weight. Valid font weight property values are: @@ -176,7 +176,7 @@ mlapptools.fontWeight(myGUI.TextArea, 600); ----------------- -#### *mlapptools*.**getChildNodeIDs**(*hWebWindow*,*widgetID*) +#### *mlapptools*.**getChildNodeIDs**(*hWin*,*widgetID*) ##### Description @@ -185,13 +185,13 @@ A method for getting all children nodes (commonly `
` elements) of a specifi ##### Examples ```MATLAB -tg = uitabgroup(uifigure()); -uitab(tg); -[win, widgetID] = mlapptools.getWebElements(tg); -[childIDs] = mlapptools.getChildNodeIDs(win, widgetID); -mlapptools.setStyle(win,'background','blue',childIDs(2)); -[childIDs] = mlapptools.getChildNodeIDs(win, childIDs(2)); -mlapptools.setStyle(win,'background','green',childIDs(4)); +hTG = uitabgroup(uifigure()); +uitab(hTG); +[hWin, widgetID] = mlapptools.getWebElements(hTG); +[childIDs] = mlapptools.getChildNodeIDs(hWin, widgetID); +mlapptools.setStyle(hWin,'background','blue',childIDs(2)); +[childIDs] = mlapptools.getChildNodeIDs(hWin, childIDs(2)); +mlapptools.setStyle(hWin,'background','green',childIDs(4)); ``` ----------------- @@ -218,7 +218,7 @@ web(['text://' fullHTML]); ----------------- -#### *mlapptools*.**getParentNodeID**(*hWebWindow*,*widgetID*) +#### *mlapptools*.**getParentNodeID**(*hWin*,*widgetID*) ##### Description @@ -230,15 +230,15 @@ Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB tg = uitabgroup(uifigure()); -[win, widgetID] = mlapptools.getWebElements(tg); -mlapptools.setStyle(win,'background','linear-gradient(red, pink, white)',... - mlapptools.getParentNodeID(win, widgetID)); +[hWin, widgetID] = mlapptools.getWebElements(tg); +mlapptools.setStyle(hWin,'background','linear-gradient(red, pink, white)',... + mlapptools.getParentNodeID(hWin, widgetID)); ``` ----------------- -#### *mlapptools*.**getWebElements**(*uiElement*) +#### *mlapptools*.**getWebElements**(*hUIElement*) ##### Description @@ -249,7 +249,7 @@ widget ID can be used to call the 4-parameter variant of [`setStyle`](#setStyle) ```MATLAB myGUI = DOMdemoGUI; -[win, widgetID] = mlapptools.getWebElements(myGUI.TextArea); +[hWin, widgetID] = mlapptools.getWebElements(myGUI.TextArea); ``` ----------------- @@ -267,13 +267,13 @@ Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB myGUI = DOMdemoGUI; -win = mlapptools.getWebWindow(myGUI.UIFigure); +hWin = mlapptools.getWebWindow(myGUI.UIFigure); ``` ----------------- -#### *mlapptools*.**getWidgetInfo**(*hWebwindow*, *widgetID*, *verbosityFlag*) +#### *mlapptools*.**getWidgetInfo**(*hWin*, *widgetID*, *verbosityFlag*) ##### Description @@ -286,8 +286,8 @@ Using the demo GUI generated by `./Demo/DOMdemoGUI.m` ```MATLAB myGUI = DOMdemoGUI; -[win, widgetID] = mlapptools.getWebElements(myGUI.TextArea); -nfo = mlapptools.getWidgetInfo(win, widgetID); +[hWin, widgetID] = mlapptools.getWebElements(myGUI.TextArea); +nfo = mlapptools.getWidgetInfo(hWin, widgetID); ``` ----------------- @@ -311,13 +311,13 @@ nfoList = mlapptools.getWidgetList(myGUI.UIFigure); ----------------- -#### *mlapptools*.**setStyle**(*uiElement*, *styleAttr*, *styleValue*) +#### *mlapptools*.**setStyle**(*hUIElement*, *styleAttr*, *styleValue*) #### *mlapptools*.**setStyle**(*hWin*, *styleAttr*, *styleValue*, *ID_object*) ##### Description -Set the style attribute `styleAttr` of the specified UI element, `'uiElement'`, to the value `styleValue`. `styleAttr` +Set the style attribute `styleAttr` of the specified UI element, `'hUIElement'`, to the value `styleValue`. `styleAttr` should be any valid CSS attribute, and `styleValue` a valid setting thereof. This method provides a general interface to change CSS style attributes, with minimal input testing and error reporting, @@ -355,10 +355,10 @@ mlapptools.setTimeout(myGUI.UIFigure, 3); % This will wait less for dojo queries ----------------- -#### *mlapptools*.**textAlign**(*uiElement*, *alignment*) +#### *mlapptools*.**textAlign**(*hUIElement*, *alignment*) ##### Description -Set the horizontal text alignment of the specified UI element, `uiElement`, to that specified by the input alignment +Set the horizontal text alignment of the specified UI element, `hUIElement`, to that specified by the input alignment string, `alignment`. Valid alignment strings are: diff --git a/mlapptools.m b/mlapptools.m index da3c4ba..cd7dda6 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -72,7 +72,7 @@ jsLibVersions = struct('dojo', dojoVersion, 'react_js', reactVersion); end % aboutJSLibs - function addClasses(uiElement, cssClasses) + function addClasses(hUIElement, cssClasses) % A method for adding CSS classes to DOM nodes. % Ensure we end up with a space-delimited list of classes: @@ -82,17 +82,17 @@ function addClasses(uiElement, cssClasses) classList = cssClasses; end - if ~isscalar(uiElement) - arrayfun(@(x)mlapptools.addClasses(x, cssClasses), uiElement); + if ~isscalar(hUIElement) + arrayfun(@(x)mlapptools.addClasses(x, cssClasses), hUIElement); else - [win, ID_struct] = mlapptools.getWebElements(uiElement); + [hWin, widgetID] = mlapptools.getWebElements(hUIElement); % Construct a dojo.js statement: classSetStr = sprintf(... 'dojo.addClass(dojo.query("[%s = ''%s'']")[0], "%s")',... - ID_struct.ID_attr, ID_struct.ID_val, classList); + widgetID.ID_attr, widgetID.ID_val, classList); % Add the class(es) to the DOM node: - win.executeJS(classSetStr); + hWin.executeJS(classSetStr); end end % addClasses @@ -115,36 +115,38 @@ function addToHead(hWin, nodeText) hWin.executeJS(['document.head.innerHTML += ', nodeText]); end % addToHead - function fontColor(uiElement, color) + function fontColor(hUIElement, color) % A method for manipulating text color. color = mlapptools.validateCSScolor(color); - [win, ID_struct] = mlapptools.getWebElements(uiElement); + [hWin, widgetID] = mlapptools.getWebElements(hUIElement); - mlapptools.setStyle(win, 'color', color, ID_struct); + mlapptools.setStyle(hWin, 'color', color, widgetID); end % fontColor - function fontWeight(uiElement, weight) + function fontWeight(hUIElement, weight) % A method for manipulating font weight, which controls how thick or % thin characters in text should be displayed. weight = mlapptools.validateFontWeight(weight); - [win, ID_struct] = mlapptools.getWebElements(uiElement); + [hWin, widgetID] = mlapptools.getWebElements(hUIElement); - mlapptools.setStyle(win, 'font-weight', weight, ID_struct); + mlapptools.setStyle(hWin, 'font-weight', weight, widgetID); end % fontWeight - function [childIDs] = getChildNodeIDs(win,ID_obj) + function [childIDs] = getChildNodeIDs(hWin, widgetID) % A method for getting all children nodes (commonly
) of a specified node. % Returns a vector WidgetID. - queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... - 'function(node){return node.childNodes;})[0];'],ID_obj.ID_attr, ID_obj.ID_val); + queryStr = sprintf([... + 'var W = dojo.query("[%s = ''%s'']").map(',... + 'function(node){return node.childNodes;})[0];'],... + widgetID.ID_attr, widgetID.ID_val); % The [0] above is required because an Array of Arrays is returned. - [~] = win.executeJS(queryStr); + [~] = hWin.executeJS(queryStr); % Try to establish an ID: - childIDs = mlapptools.establishIdentities(win); + childIDs = mlapptools.establishIdentities(hWin); % "Clear" the temporary JS variable - win.executeJS('W = undefined'); + hWin.executeJS('W = undefined'); end % getChildNodeIDs function [fullHTML] = getHTML(hFigOrWin) @@ -155,12 +157,12 @@ function fontWeight(uiElement, weight) % but can be useful for other non-uifigure webwindows. %% Obtain webwindow handle: if isa(hFigOrWin,'matlab.ui.Figure') - win = mlapptools.getWebWindow(hFigOrWin); + hWin = mlapptools.getWebWindow(hFigOrWin); indepWW = false; else - win = hFigOrWin; % rename the handle + hWin = hFigOrWin; % rename the handle %% Attempt to determine if this is an "independent" webwindow: - hF = mlapptools.figFromWebwindow(win); + hF = mlapptools.figFromWebwindow(hWin); if isempty(hF) indepWW = true; else @@ -169,14 +171,14 @@ function fontWeight(uiElement, weight) end %% Get HTML according to webwindow type: if indepWW - [~,hWB] = web(win.URL, '-new'); + [~,hWB] = web(hWin.URL, '-new'); fullHTML = hWB.getHtmlText(); close(hWB); %% TODO: Try to fix css paths: % See: https://stackoverflow.com/q/50944935/ %{ % Get all elements: - win.executeJS('dojo.query("link")') + hWin.executeJS('dojo.query("link")') % Convert relative paths to absolute: % Replace paths in HTML: @@ -184,7 +186,7 @@ function fontWeight(uiElement, weight) %} else % Get the outer html: - fullHTML = win.executeJS('document.documentElement.outerHTML'); + fullHTML = hWin.executeJS('document.documentElement.outerHTML'); % Replace some strings for conversion to work well: fullHTML = strrep(fullHTML,'%','%%'); fullHTML = strrep(fullHTML,'><','>\n<'); @@ -204,29 +206,30 @@ function fontWeight(uiElement, weight) end end % getHTML - function [parentID] = getParentNodeID(win,ID_obj) + function [parentID] = getParentNodeID(hWin, widgetID) % A method for getting the parent node (commonly
) of a specified node. % Returns a scalar WidgetID. queryStr = sprintf(['var W = dojo.query("[%s = ''%s'']").map(',... - 'function(node){return node.parentNode;});'],ID_obj.ID_attr, ID_obj.ID_val); - [~] = win.executeJS(queryStr); + 'function(node){return node.parentNode;});'],widgetID.ID_attr, widgetID.ID_val); + [~] = hWin.executeJS(queryStr); % Try to establish an ID: - parentID = mlapptools.establishIdentities(win); + parentID = mlapptools.establishIdentities(hWin); % "Clear" the temporary JS variable - win.executeJS('W = undefined'); + hWin.executeJS('W = undefined'); end % getParentNodeID - function [ID_obj] = getTableCellID(hTable, r, c) + function [widgetID] = getTableCellID(hUITable, r, c) % This method returns one or more ID objects, corresponding to specific cells in a % uitable, as defined by the cells' row and column indices. %% Constants: TABLE_CLASS_NAME = 'matlab.ui.control.Table'; CELL_ID_PREFIX = {'variableeditor_views_editors_UITableEditor_'}; %% Input tests: - assert( isa(hTable, TABLE_CLASS_NAME) && ishandle(hTable), 'Invalid uitable handle!'); + assert( isa(hUITable, TABLE_CLASS_NAME) && ishandle(hUITable),... + 'Invalid uitable handle!'); nR = numel(r); nC = numel(c); assert( nR == nC, 'The number of elements in r and c must be the same!'); - sz = size(hTable.Data); + sz = size(hUITable.Data); assert( all(r <= sz(1)), 'One or more requested rows are out-of-bounds!'); assert( all(c <= sz(2)), 'One or more requested columns are out-of-bounds!'); %% Computing the offset @@ -243,46 +246,48 @@ function fontWeight(uiElement, weight) % IDs by the total amount of elements in all preceding arrays, which is 9. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Get siblings (flipping to negate that newer children appear first) - hC = flip(hTable.Parent.Children); + hC = flip(hUITable.Parent.Children); % Find which of them is a table: hC = hC( arrayfun(@(x)isa(x, TABLE_CLASS_NAME), hC) ); % Find the position of the current table in the list, and count the elements of % all preceding tables: - offset = sum(arrayfun(@(x)numel(x.Data), hC(1:find(hC == hTable)-1))); + offset = sum(arrayfun(@(x)numel(x.Data), hC(1:find(hC == hUITable)-1))); % Compute indices, r and c are reversed due to row-major ordering of the IDs idx = sub2ind(sz, c, r) + offset - 1; % -1 because JS IDs are 0-based idc = strcat(CELL_ID_PREFIX, num2str(idx(:))); % Preallocation: - ID_obj(nR,1) = WidgetID; + widgetID(nR,1) = WidgetID; % Get the window handle so we could execute some JS commands: - hWin = mlapptools.getWebWindow(hTable); + hWin = mlapptools.getWebWindow(hUITable); % ID array population: for indI = 1:numel(idx) jsCommand = sprintf('dojo.byId(%s).childNodes[0].id', idc{indI}); textFieldID = hWin.executeJS(jsCommand); - ID_obj(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, textFieldID(2:end-1) ); + widgetID(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, textFieldID(2:end-1) ); end end % getTableCellID - function [win, widgetID] = getWebElements(uiElement) + function [hWin, widgetID] = getWebElements(hUIElement) % A method for obtaining the webwindow handle and the widget ID corresponding - % to the provided uifigure control. + % to the provided ui control (aka web component). + % Get a handle to the webwindow - win = mlapptools.getWebWindow(uiElement); - mlapptools.waitTillWebwindowLoaded(win, ancestor(uiElement, 'matlab.ui.Figure') ); + hWin = mlapptools.getWebWindow(hUIElement); + mlapptools.waitTillWebwindowLoaded( hWin, ... + ancestor(hUIElement, 'matlab.ui.Figure') ); % Find which element of the DOM we want to edit - switch uiElement.Type + switch hUIElement.Type case 'uitreenode' - p = uiElement.Parent; + p = hUIElement.Parent; if ~isa(p,'matlab.ui.container.Tree') p.expand(); % The row must be visible to apply changes end warnState = mlapptools.toggleWarnings('off'); - widgetID = WidgetID('data-test-id', char(struct(uiElement).NodeId)); + widgetID = WidgetID('data-test-id', char(struct(hUIElement).NodeId)); warning(warnState); % Restore warning state case {'uipanel','figure','uitabgroup','uitab'} - widgetID = WidgetID('data-tag', mlapptools.getDataTag(uiElement)); + widgetID = WidgetID('data-tag', mlapptools.getDataTag(hUIElement)); case 'uitable' TAB_PREFIX = "mgg_"; % uitables are inconsistent with other elements, their id always starts with @@ -291,20 +296,20 @@ function fontWeight(uiElement, weight) % nodes having a property that starts with a certain string: E[foo^="bar"] % web(['http://dojotoolkit.org/reference-guide/1.10/dojo/query.html',... % '#additional-selectors-supported-by-lite-engine'], '-browser'); - [~,tmp] = mlapptools.getWidgetList( ancestor(uiElement,'figure') ); + [~,tmp] = mlapptools.getWidgetList( ancestor(hUIElement,'figure') ); widgetID = arrayfun(@(x)WidgetID(mlapptools.DEF_ID_ATTRIBUTE, x), ... string(tmp.id(contains(tmp.id, TAB_PREFIX)))); otherwise % default: - widgetID = mlapptools.getWidgetID(win, mlapptools.getDataTag(uiElement)); + widgetID = mlapptools.getWidgetID(hWin, mlapptools.getDataTag(hUIElement)); end end % getWebElements - function [win] = getWebWindow(hUIObj) + function [hWin] = getWebWindow(hUIElement) warnState = mlapptools.toggleWarnings('off'); % Make sure we got a valid handle % Check to make sure we're addressing the parent figure window, % catches the case where the parent is a UIPanel or similar - hUIFig = ancestor(hUIObj, 'figure'); + hUIFig = ancestor(hUIElement, 'figure'); mlapptools.waitTillFigureLoaded(hUIFig); % Since the above checks if a Controller exists, the below should work. @@ -313,16 +318,16 @@ function fontWeight(uiElement, weight) % Check for Controller version: switch subsref(ver('matlab'), substruct('.','Version')) case {'9.0','9.1'} % R2016a or R2016b - win = hController.Container.CEF; + hWin = hController.Container.CEF; otherwise % R2017a onward - win = struct(hController.PlatformHost).CEF; + hWin = struct(hController.PlatformHost).CEF; end warning(warnState); % Restore warning state end % getWebWindow - function [nfo] = getWidgetInfo(win, widgetID, verboseFlag) + function [nfo] = getWidgetInfo(hWin, widgetID, verboseFlag) % A method for gathering information about a specific dijit widget, if its % HTML div id is known. if ~strcmp(widgetID.ID_attr,'widgetid') @@ -339,11 +344,11 @@ function fontWeight(uiElement, weight) verboseFlag = false; end %% Querying dijit - win.executeJS(['var W; require(["dijit/registry"], '... + hWin.executeJS(['var W; require(["dijit/registry"], '... 'function(registry){W = registry.byId("' widgetID.ID_val '");}); W = [W];']); % Decoding try - nfo = mlapptools.decodeDijitRegistryResult(win,verboseFlag); + nfo = mlapptools.decodeDijitRegistryResult(hWin,verboseFlag); catch ME switch ME.identifier case 'mlapptools:decodeDijitRegistryResult:noSuchWidget' @@ -355,7 +360,7 @@ function fontWeight(uiElement, weight) nfo = []; end % "Clear" the temporary JS variable - win.executeJS('W = undefined'); + hWin.executeJS('W = undefined'); end % getWidgetInfo function varargout = getWidgetList(hUIFig, verboseFlag) @@ -371,13 +376,13 @@ function fontWeight(uiElement, weight) verboseFlag = false; end %% Process uifigure: - win = mlapptools.getWebWindow(hUIFig); + hWin = mlapptools.getWebWindow(hUIFig); % Extract widgets from dijit registry: - win.executeJS(['var W; require(["dijit/registry"], '... + hWin.executeJS(['var W; require(["dijit/registry"], '... ' function(registry){W = registry.toArray();});']); - widgets = mlapptools.decodeDijitRegistryResult(win, verboseFlag); + widgets = mlapptools.decodeDijitRegistryResult(hWin, verboseFlag); % "Clear" the temporary JS variable - win.executeJS('W = undefined'); + hWin.executeJS('W = undefined'); %% Assign outputs: varargout{1} = widgets; if nargout == 2 @@ -397,7 +402,7 @@ function fontWeight(uiElement, weight) % 3-parameter call: % widgetID = setStyle(hControl, styleAttr, styleValue) % 4-parameter call: - % setStyle(hWin, styleAttr, styleValue, ID_obj) + % setStyle(hWin, styleAttr, styleValue, widgetID) narginchk(3,4); % Unpack inputs: @@ -406,24 +411,24 @@ function fontWeight(uiElement, weight) switch nargin case 3 - hControl = varargin{1}; + hUIElement = varargin{1}; % Get a handle to the webwindow - [win, ID_obj] = mlapptools.getWebElements(hControl); + [hWin, widgetID] = mlapptools.getWebElements(hUIElement); case 4 % By the time we have a WidgetID object, the webwindow handle is available - win = varargin{1}; - ID_obj = varargin{4}; + hWin = varargin{1}; + widgetID = varargin{4}; end - % Handle the case of a non-scalar ID_obj recursively: - if ~isscalar(ID_obj) - arrayfun(@(x)mlapptools.setStyle(win, styleAttr, styleValue, x), ID_obj); + % Handle the case of a non-scalar widgetID recursively: + if ~isscalar(widgetID) + arrayfun(@(x)mlapptools.setStyle(hWin, styleAttr, styleValue, x), widgetID); else styleSetStr = sprintf('dojo.style(dojo.query("[%s = ''%s'']")[0], "%s", "%s")',... - ID_obj.ID_attr, ID_obj.ID_val, styleAttr, styleValue); + widgetID.ID_attr, widgetID.ID_val, styleAttr, styleValue); % ^ this might result in junk if widgetId=='null'. try - win.executeJS(styleSetStr); + hWin.executeJS(styleSetStr); % ^ this might crash in case of invalid styleAttr/styleValue. catch ME % Test for "Invalid or unexpected token": @@ -434,7 +439,7 @@ function fontWeight(uiElement, weight) % Assign outputs: if nargout >= 1 - varargout{1} = ID_obj; + varargout{1} = widgetID; end end % setStyle @@ -443,14 +448,14 @@ function setTimeout(hUIFig, newTimeoutInSec) setappdata(hUIFig, mlapptools.TAG_TIMEOUT, newTimeoutInSec); end - function textAlign(uiElement, alignment) + function textAlign(hUIElement, alignment) % A method for manipulating text alignment. alignment = lower(alignment); mlapptools.validateAlignmentStr(alignment) - [win, ID_struct] = mlapptools.getWebElements(uiElement); + [hWin, widgetID] = mlapptools.getWebElements(hUIElement); - mlapptools.setStyle(win, 'textAlign', alignment, ID_struct); + mlapptools.setStyle(hWin, 'textAlign', alignment, widgetID); end % textAlign function unlockUIFig(hUIFig) @@ -542,7 +547,7 @@ function unlockUIFig(hUIFig) end % checkCert end % unlockUIFig - function win = waitForFigureReady(hUIFig) + function hWin = waitForFigureReady(hUIFig) % This blocking method waits until a UIFigure and its widgets have fully loaded. %% Make sure that the handle is valid: assert(mlapptools.isUIFigure(hUIFig),... @@ -555,15 +560,15 @@ function unlockUIFig(hUIFig) mlapptools.waitTillFigureLoaded(hUIFig); %% Make sure that Dojo is ready: % Get a handle to the webwindow - win = mlapptools.getWebWindow(hUIFig); - mlapptools.waitTillWebwindowLoaded(win, hUIFig); + hWin = mlapptools.getWebWindow(hUIFig); + mlapptools.waitTillWebwindowLoaded(hWin, hUIFig); end % waitForFigureReady end % Public Static Methods methods (Static = true, Access = private) - function ME = checkJavascriptSyntaxError(ME,styleSetStr) + function ME = checkJavascriptSyntaxError(ME, styleSetStr) if (strcmp(ME.identifier,'cefclient:webwindow:jserror')) c = strfind(ME.message,'Uncaught SyntaxError:'); if ~isempty(c) @@ -575,31 +580,31 @@ function unlockUIFig(hUIFig) end end % checkJavascriptSyntaxError - function widgets = decodeDijitRegistryResult(win, verboseFlag) + function widgets = decodeDijitRegistryResult(hWin, verboseFlag) % As this method relies heavily on jsondecode, it is only supported on R >= 2016b - assert(strcmp('true', win.executeJS(... + assert(strcmp('true', hWin.executeJS(... 'this.hasOwnProperty("W") && W !== undefined && W instanceof Array && W.length > 0')),... 'mlapptools:decodeDijitRegistryResult:noSuchWidget',... 'The dijit registry doesn''t contain the specified widgetID.'); % Now that we know that W exists, let's try to decode it. - n = str2double(win.executeJS('W.length;')); + n = str2double(hWin.executeJS('W.length;')); widgets = cell(n,1); % Get the JSON representing the widget, then try to decode, while catching circular references for ind1 = 1:n try - widgets{ind1} = jsondecode(win.executeJS(sprintf('W[%d]', ind1-1))); + widgets{ind1} = jsondecode(hWin.executeJS(sprintf('W[%d]', ind1-1))); catch % handle circular references: if verboseFlag - disp(['Node #' num2str(ind1-1) ' with id ' win.executeJS(sprintf('W[%d].id', ind1-1))... + disp(['Node #' num2str(ind1-1) ' with id ' hWin.executeJS(sprintf('W[%d].id', ind1-1))... ' could not be fully converted. Attempting fallback...']); end - props = jsondecode(win.executeJS(sprintf('Object.keys(W[%d])', ind1-1))); + props = jsondecode(hWin.executeJS(sprintf('Object.keys(W[%d])', ind1-1))); tmp = mlapptools.emptyStructWithFields(props); validProps = fieldnames(tmp); for indP = 1:numel(validProps) try - tmp.(validProps{indP}) = jsondecode(win.executeJS(sprintf(['W[%d].' props{indP}], ind1-1))); + tmp.(validProps{indP}) = jsondecode(hWin.executeJS(sprintf(['W[%d].' props{indP}], ind1-1))); catch % Fallback could be executed recursively for all problematic field % (to keep the most data), but for now do nothing. @@ -622,9 +627,9 @@ function unlockUIFig(hUIFig) end % emptyStructWithFields - function [widgetID] = establishIdentities(win) % throws AssertionError + function [widgetID] = establishIdentities(hWin) % throws AssertionError % A method for generating WidgetID objects from a list of DOM nodes. - assert(strcmp('true', win.executeJS([... + assert(strcmp('true', hWin.executeJS([... 'this.hasOwnProperty("W") && W !== undefined && ' ... '(W instanceof NodeList || W instanceof Array) && W.length > 0'])),... 'mlapptools:establishIdentities:noSuchNode',... @@ -633,12 +638,12 @@ function unlockUIFig(hUIFig) attrs = {'widgetid', 'id', 'data-tag', 'data-reactid'}; nA = numel(attrs); %% Preallocate output: - widgetID(win.executeJS('W.length') - '0', 1) = WidgetID; % "str2double" + widgetID(hWin.executeJS('W.length') - '0', 1) = WidgetID; % "str2double" %% for indW = 1:numel(widgetID) for indA = 1:nA % Get the attribute value: - ID = win.executeJS(sprintf('W[%d].getAttribute("%s")', indW-1, attrs{indA})); + ID = hWin.executeJS(sprintf('W[%d].getAttribute("%s")', indW-1, attrs{indA})); % Test result validity: if ~strcmp(ID,'null') % Create a WidgetID object and proceed to the next element in W: @@ -653,13 +658,13 @@ function unlockUIFig(hUIFig) end end % establishIdentity - function [data_tag] = getDataTag(uiElement) + function [data_tag] = getDataTag(hUIElement) warnState = mlapptools.toggleWarnings('off'); - data_tag = char( struct(uiElement).Controller.ProxyView.PeerNode.getId() ); + data_tag = char( struct(hUIElement).Controller.ProxyView.PeerNode.getId() ); warning(warnState); end % getDataTag - function hFig = figFromWebwindow(hWebwindow) + function hFig = figFromWebwindow(hWin) % Using this method is discouraged as it's relatively computation-intensive. % Since the figure handle is not a property of the webwindow or its children % (to our best knowledge), we must list all figures and check which of them @@ -676,78 +681,78 @@ function unlockUIFig(hUIFig) hUIFigs = hUIFigs(strcmp({hUIFigs.Visible},'on')); % Hidden figures are ignored ww = arrayfun(@mlapptools.getWebWindow, hUIFigs); warning(warnState); % Restore warning state - hFig = hFigs(hWebwindow == ww); + hFig = hFigs(hWin == ww); end % figFromWebwindow - function [ID_obj] = getWidgetID(win, data_tag) + function [widgetID] = getWidgetID(hWin, dataTag) % This method returns a structure containing some uniquely-identifying information % about a DOM node. widgetquerystr = sprintf(... - 'dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', data_tag); + 'dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', dataTag); try % should work for most UI objects - ID = win.executeJS(widgetquerystr); - ID_obj = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); + ID = hWin.executeJS(widgetquerystr); + widgetID = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); catch % fallback for problematic objects warning('This widget is unsupported.'); - % ID_obj = mlapptools.getWidgetIDFromDijit(win, data_tag); + % widgetID = mlapptools.getWidgetIDFromDijit(hWin, data_tag); end end % getWidgetID - function ID_obj = getWidgetIDFromDijit(win, data_tag) + function widgetID = getWidgetIDFromDijit(hWin, dataTag) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EXPERIMENTAL METHOD!!! - win.executeJS(['var W; require(["dijit/registry"], '... + hWin.executeJS(['var W; require(["dijit/registry"], '... 'function(registry){W = registry.toArray().map(x => x.domNode.childNodes);});']); - nWidgets = jsondecode(win.executeJS('W.length')); + nWidgets = jsondecode(hWin.executeJS('W.length')); try for ind1 = 0:nWidgets-1 - nChild = jsondecode(win.executeJS(sprintf('W[%d].length',ind1))); + nChild = jsondecode(hWin.executeJS(sprintf('W[%d].length',ind1))); for ind2 = 0:nChild-1 - tmp = win.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); + tmp = hWin.executeJS(sprintf('W[%d][%d].dataset',ind1,ind2)); if isempty(tmp) continue else tmp = jsondecode(tmp); end - if isfield(tmp,'tag') && strcmp(tmp.tag,data_tag) - ID = win.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); + if isfield(tmp,'tag') && strcmp(tmp.tag,dataTag) + ID = hWin.executeJS(sprintf('dojo.getAttr(W[%d][%d].parentNode,"widgetid")',ind1,ind2)); error('Bailout!'); end end end - ID_obj = WidgetID('',''); + widgetID = WidgetID('',''); catch % Fix for the case of top-level tree nodes: switch tmp.type case 'matlab.ui.container.TreeNode' - tmp = jsondecode(win.executeJS(sprintf(... + tmp = jsondecode(hWin.executeJS(sprintf(... 'dojo.byId(%s).childNodes[0].childNodes[0].childNodes[0].childNodes[%d].dataset',... ID(2:end-1),ind2-1))); - ID_obj = WidgetID('data-reactid', tmp.reactid); + widgetID = WidgetID('data-reactid', tmp.reactid); end end end % getWidgetIDFromDijit - function to = getTimeout(hFig) - if isempty(hFig) || ~isa(hFig, 'matlab.ui.Figure') + function to = getTimeout(hUIFig) + if isempty(hUIFig) || ~isa(hUIFig, 'matlab.ui.Figure') to = mlapptools.QUERY_TIMEOUT; else - to = getappdata(hFig, mlapptools.TAG_TIMEOUT); + to = getappdata(hUIFig, mlapptools.TAG_TIMEOUT); if isempty(to), to = mlapptools.QUERY_TIMEOUT; end end end % getTimeout - function tf = isUIFigure(hList) + function tf = isUIFigure(hFigList) tf = arrayfun(@(x)isa(x,'matlab.ui.Figure') && ... - isstruct(struct(x).ControllerInfo), hList); + isstruct(struct(x).ControllerInfo), hFigList); end % isUIFigure - function oldState = toggleWarnings(togglestr) + function oldState = toggleWarnings(toggleStr) OJF = 'MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame'; SOO = 'MATLAB:structOnObject'; if nargout > 0 oldState = [warning('query',OJF); warning('query',SOO)]; end - switch lower(togglestr) + switch lower(toggleStr) case 'on' warning('on',OJF); warning('on',SOO); @@ -788,7 +793,7 @@ function validateAlignmentStr(alignment) end end % validateAlignmentStr - function [newcolor] = validateCSScolor(newcolor) + function [color] = validateCSScolor(color) % TODO end % validateCSScolor @@ -816,12 +821,12 @@ function validateAlignmentStr(alignment) end end % validateFontWeight - function waitTillFigureLoaded(hFig) + function waitTillFigureLoaded(hUIFig) % A blocking method that ensures a UIFigure has fully loaded. warnState = mlapptools.toggleWarnings('off'); - to = mlapptools.getTimeout(hFig); + to = mlapptools.getTimeout(hUIFig); tic - while (toc < to) && isempty(struct(hFig).Controller) + while (toc < to) && isempty(struct(hUIFig).Controller) pause(0.01) end if toc > to @@ -836,18 +841,18 @@ function waitTillFigureLoaded(hFig) warning(warnState); end % waitTillFigureLoaded - function waitTillWebwindowLoaded(hWebwindow, hFig) + function waitTillWebwindowLoaded(hWin, hUIFig) % A blocking method that ensures a certain webwindow has fully loaded. try if nargin < 2 - hFig = mlapptools.figFromWebwindow(hWebwindow); + hUIFig = mlapptools.figFromWebwindow(hWin); end - to = mlapptools.getTimeout(hFig); + to = mlapptools.getTimeout(hUIFig); catch % possible workaround for R2017a: to = mlapptools.getTimeout([]); end tic - while (toc < to) && ~jsondecode(hWebwindow.executeJS(... + while (toc < to) && ~jsondecode(hWin.executeJS(... 'this.hasOwnProperty("require") && require !== undefined && typeof(require) === "function"')) pause(0.01) end @@ -860,14 +865,14 @@ function waitTillWebwindowLoaded(hWebwindow, hFig) 'Please let the developers know through GitHub.'], ... to); else - hWebwindow.executeJS('require(["dojo/ready"], function(ready){});'); + hWin.executeJS('require(["dojo/ready"], function(ready){});'); end end % waitTillWebwindowLoaded - function htmlRootPath = getFullPathFromWW(win) + function htmlRootPath = getFullPathFromWW(hWin) % Get a local href value, e.g. from an included main.css - href = win.executeJS('document.body.getElementsByTagName("link")[0].href'); - htmlRootPath = win.executeJS(['var link = document.createElement("a"); link.href = ' href ';']); + href = hWin.executeJS('document.body.getElementsByTagName("link")[0].href'); + htmlRootPath = hWin.executeJS(['var link = document.createElement("a"); link.href = ' href ';']); end end % Private Static Methods @@ -877,11 +882,11 @@ function waitTillWebwindowLoaded(hWebwindow, hFig) %{ --- Useful debugging commands --- -jsprops = sort(jsondecode(win.executeJS('Object.keys(this)'))); +jsprops = sort(jsondecode(hWin.executeJS('Object.keys(this)'))); ReactJS: -win.executeJS('var R = require("react/react.min"); Object.keys(R)') -win.executeJS('var R = require("react/react-dom.min"); Object.keys(R)') +hWin.executeJS('var R = require("react/react.min"); Object.keys(R)') +hWin.executeJS('var R = require("react/react-dom.min"); Object.keys(R)') %} \ No newline at end of file From 70c1ecc13ddef190c9b9a992b89fcdfa95d26c7b Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Fri, 17 Aug 2018 20:09:18 +0300 Subject: [PATCH 12/18] Adding functionSignatures.json (part 1) See also: https://www.mathworks.com/help/matlab/matlab_prog/customize-code-suggestions-and-completions.html --- functionSignatures.json | 221 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 functionSignatures.json diff --git a/functionSignatures.json b/functionSignatures.json new file mode 100644 index 0000000..9228e0b --- /dev/null +++ b/functionSignatures.json @@ -0,0 +1,221 @@ +{ + "_schemaVersion": "1.0.0", + "_typedefs" : { + "PLACEHOLDER_STRUCT": { + "type": "struct", + "fields": [ + {"name": "PLACEHOLDER1", "type": "char"}, + {"name": "PLACEHOLDER2", "type": ["double","size=1,1"]} + ] + }, + "PLACEHOLDER_CELL": { + "type": "cell", + "elements": { + "type": "struct:PLACEHOLDER_STRUCT" + } + } +}, + "mlapptools.aboutJSLibs": + { + "outputs": + [ + { + "name":"jsLibVersions", + "type":"struct" + } + ] + }, + "mlapptools.addClasses": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"cssClasses", + "kind":"required", + "purpose":"Classes to add", + "type":[["char"],["string"],["cell"]] + } + ] + }, + "mlapptools.addToHead": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"nodeText", + "kind":"required", + "purpose":"'s outerHTML", + "type":[["char"],["string"]] + } + ] + }, + "mlapptools.fontColor": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"color", + "kind":"required", + "purpose":"CSS color", + "type":[["char"],["string"]] + } + ] + }, + "mlapptools.fontWeight": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"color", + "kind":"required", + "purpose":"CSS font weight", + "type":[["char"],["string"]] + } + ] + }, + "mlapptools.getChildNodeIDs": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"widgetID", + "kind":"required", + "purpose":"Widget ID object(s)", + "type":["WidgetID"] + } + ], + "outputs": + [ + { + "name":"childIDs", + "type":["WidgetID"] + } + ] + }, + "mlapptools.getHTML": + { + "inputs": + [ + { + "name":"hFigOrWin", + "kind":"required", + "purpose":"UIFigure or webWindow handle", + "type":["matlab.internal.webwindow", "matlab.ui.Figure"] + } + ], + "outputs": + [ + { + "name":"fullHTML", + "type":["char"] + } + ] + }, + "mlapptools.getWebElements": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"Web component handle", + "type":["matlab.ui.control.WebComponent"] + } + ], + "outputs": + [ + { + "name":"hWin", + "type":["matlab.internal.webwindow"] + }, + { + "name":"widgetID", + "type":["WidgetID"] + } + ] + }, + "mlapptools.setStyle": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"styleAttr", + "kind":"required", + "purpose":"CSS attribute name", + "type":[["char"],["string"]] + }, + { + "name":"styleValue", + "kind":"required", + "purpose":"CSS attribute value", + "type":[["char"],["string"]] + } + ] + }, + "mlapptools.setStyle": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"styleAttr", + "kind":"required", + "purpose":"CSS attribute name", + "type":[["char"],["string"]] + }, + { + "name":"styleValue", + "kind":"required", + "purpose":"CSS attribute value", + "type":[["char"],["string"]] + }, + { + "name":"widgetID", + "kind":"required", + "purpose":"Widget ID object(s)", + "type":["WidgetID"] + } + ] + } +} \ No newline at end of file From 89998c9ede52f191d6dd2663c0f3cd204756f155 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 28 Aug 2018 18:38:26 +0300 Subject: [PATCH 13/18] Adding functionSignatures.json (part 2) + Also fixed some comments in mlapptools. + TODO: add `getTableCellID` to the readme. --- functionSignatures.json | 862 ++++++++++++++++++++++++++++++++-------- mlapptools.m | 200 +++++----- 2 files changed, 803 insertions(+), 259 deletions(-) diff --git a/functionSignatures.json b/functionSignatures.json index 9228e0b..2018079 100644 --- a/functionSignatures.json +++ b/functionSignatures.json @@ -14,7 +14,8 @@ "type": "struct:PLACEHOLDER_STRUCT" } } -}, + }, + "%%%%%%%%%%%%%%%1": "PUBLIC STATIC METHODS START HERE %%%%%%%%%%%%%%%%", "mlapptools.aboutJSLibs": { "outputs": @@ -27,112 +28,112 @@ }, "mlapptools.addClasses": { - "inputs": - [ - { - "name":"hUIElement", - "kind":"required", - "purpose":"hUIElement handle", - "type":["matlab.ui.control.internal.model.ComponentModel"] - }, - { - "name":"cssClasses", - "kind":"required", - "purpose":"Classes to add", - "type":[["char"],["string"],["cell"]] - } - ] + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"cssClasses", + "kind":"required", + "purpose":"Classes to add", + "type":[["char"],["string"],["cell"]] + } + ] }, "mlapptools.addToHead": { - "inputs": - [ - { - "name":"hWin", - "kind":"required", - "purpose":"webWindow handle", - "type":["matlab.internal.webwindow"] - }, - { - "name":"nodeText", - "kind":"required", - "purpose":"'s outerHTML", - "type":[["char"],["string"]] - } - ] + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"nodeText", + "kind":"required", + "purpose":"'s outerHTML", + "type":[["char"],["string"]] + } + ] }, "mlapptools.fontColor": { - "inputs": - [ - { - "name":"hUIElement", - "kind":"required", - "purpose":"hUIElement handle", - "type":["matlab.ui.control.internal.model.ComponentModel"] - }, - { - "name":"color", - "kind":"required", - "purpose":"CSS color", - "type":[["char"],["string"]] - } - ] + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"color", + "kind":"required", + "purpose":"CSS color", + "type":[["char"],["string"]] + } + ] }, "mlapptools.fontWeight": { - "inputs": - [ - { - "name":"hUIElement", - "kind":"required", - "purpose":"hUIElement handle", - "type":["matlab.ui.control.internal.model.ComponentModel"] - }, - { - "name":"color", - "kind":"required", - "purpose":"CSS font weight", - "type":[["char"],["string"]] - } - ] + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"color", + "kind":"required", + "purpose":"CSS font weight", + "type":[["char"],["string"]] + } + ] }, "mlapptools.getChildNodeIDs": { - "inputs": - [ - { - "name":"hWin", - "kind":"required", - "purpose":"webWindow handle", - "type":["matlab.internal.webwindow"] - }, - { - "name":"widgetID", - "kind":"required", - "purpose":"Widget ID object(s)", - "type":["WidgetID"] - } - ], - "outputs": - [ - { - "name":"childIDs", - "type":["WidgetID"] - } - ] - }, + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"widgetID", + "kind":"required", + "purpose":"Widget ID object(s)", + "type":["WidgetID"] + } + ], + "outputs": + [ + { + "name":"childIDs", + "type":["WidgetID"] + } + ] + }, "mlapptools.getHTML": { - "inputs": - [ - { - "name":"hFigOrWin", - "kind":"required", - "purpose":"UIFigure or webWindow handle", - "type":["matlab.internal.webwindow", "matlab.ui.Figure"] - } - ], + "inputs": + [ + { + "name":"hFigOrWin", + "kind":"required", + "purpose":"UIFigure or webWindow handle", + "type":["matlab.internal.webwindow", "matlab.ui.Figure"] + } + ], "outputs": [ { @@ -141,81 +142,618 @@ } ] }, + "mlapptools.getParentNodeID": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"widgetID", + "kind":"required", + "purpose":"Widget ID object(s)", + "type":["WidgetID"] + } + ], + "outputs": + [ + { + "name":"parentID", + "type":["WidgetID"] + } + ] + }, + "mlapptools.getTableCellID": + { + "inputs": + [ + { + "name":"hUITable", + "kind":"required", + "purpose":"UITable handle", + "type":["matlab.ui.control.Table"] + }, + { + "name":"r", + "kind":"required", + "purpose":"Row indices", + "type":["numeric"] + }, + { + "name":"c", + "kind":"required", + "purpose":"Column indices", + "type":["numeric"] + } + ], + "outputs": + [ + { + "name":"widgetIDs", + "type":["WidgetID"] + } + ] + }, "mlapptools.getWebElements": { - "inputs": - [ - { - "name":"hUIElement", - "kind":"required", - "purpose":"Web component handle", - "type":["matlab.ui.control.WebComponent"] - } - ], - "outputs": - [ - { - "name":"hWin", - "type":["matlab.internal.webwindow"] - }, - { - "name":"widgetID", - "type":["WidgetID"] - } - ] - }, + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"Web component handle", + "type":["matlab.ui.control.WebComponent"] + } + ], + "outputs": + [ + { + "name":"hWin", + "type":["matlab.internal.webwindow"] + }, + { + "name":"widgetID", + "type":["WidgetID"] + } + ] + }, + "mlapptools.getWebWindow": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"Web component handle", + "type":["matlab.ui.control.WebComponent"] + } + ], + "outputs": + [ + { + "name":"hWin", + "type":["matlab.internal.webwindow"] + } + ] + }, + "mlapptools.getWidgetInfo": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"widgetID", + "kind":"required", + "purpose":"Widget ID object(s)", + "type":["WidgetID"] + }, + { + "name":"verboseFlag", + "kind":"optional", + "purpose":"Log printing setting", + "type":["boolean", "scalar"] + } + ], + "outputs": + [ + { + "name":"nfo", + "type":["struct"] + } + ] + }, + "mlapptools.getWidgetList": + { + "inputs": + [ + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + }, + { + "name":"verboseFlag", + "kind":"optional", + "purpose":"Log printing setting", + "type":["boolean", "scalar"] + } + ], + "outputs": + [ + { + "name":"widgetInfoStruct", + "type":["struct"] + }, + { + "name":"widgetInfoTable", + "type":["table"] + } + ] + }, "mlapptools.setStyle": { - "inputs": - [ - { - "name":"hUIElement", - "kind":"required", - "purpose":"hUIElement handle", - "type":["matlab.ui.control.internal.model.ComponentModel"] - }, - { - "name":"styleAttr", - "kind":"required", - "purpose":"CSS attribute name", - "type":[["char"],["string"]] - }, - { - "name":"styleValue", - "kind":"required", - "purpose":"CSS attribute value", - "type":[["char"],["string"]] - } - ] + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"styleAttr", + "kind":"required", + "purpose":"CSS attribute name", + "type":[["char"],["string"]] + }, + { + "name":"styleValue", + "kind":"required", + "purpose":"CSS attribute value", + "type":[["char"],["string"]] + } + ] }, "mlapptools.setStyle": { - "inputs": - [ - { - "name":"hWin", - "kind":"required", - "purpose":"webWindow handle", - "type":["matlab.internal.webwindow"] - }, - { - "name":"styleAttr", - "kind":"required", - "purpose":"CSS attribute name", - "type":[["char"],["string"]] - }, - { - "name":"styleValue", - "kind":"required", - "purpose":"CSS attribute value", - "type":[["char"],["string"]] - }, - { - "name":"widgetID", - "kind":"required", - "purpose":"Widget ID object(s)", - "type":["WidgetID"] - } - ] + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"styleAttr", + "kind":"required", + "purpose":"CSS attribute name", + "type":[["char"],["string"]] + }, + { + "name":"styleValue", + "kind":"required", + "purpose":"CSS attribute value", + "type":[["char"],["string"]] + }, + { + "name":"widgetID", + "kind":"required", + "purpose":"Widget ID object(s)", + "type":["WidgetID"] + } + ] + }, + "mlapptools.setTimeout": + { + "inputs": + [ + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + }, + { + "name":"newTimeoutInSec", + "kind":"required", + "purpose":"New timeout setting", + "type":["numeric", "scalar"] + } + ] + }, + "mlapptools.textAlign": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"hUIElement handle", + "type":["matlab.ui.control.internal.model.ComponentModel"] + }, + { + "name":"alignment", + "kind":"required", + "purpose":"CSS text alignment", + "type":[["char"],["string"]] + } + ] + }, + "mlapptools.unlockUIFig": + { + "inputs": + [ + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + } + ] + }, + "mlapptools.waitForFigureReady": + { + "inputs": + [ + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + } + ], + "outputs": + [ + { + "name":"hWin", + "type":["matlab.internal.webwindow"] + } + ] + }, + "%%%%%%%%%%%%%%%2": "PRIVATE STATIC METHODS START HERE %%%%%%%%%%%%%%%%", + "mlapptools.checkJavascriptSyntaxError": + { + "inputs": + [ + { + "name":"ME", + "kind":"required", + "purpose":"Exception object", + "type":["MException"] + }, + { + "name":"styleSetStr", + "kind":"required", + "purpose":"Dojo command", + "type":["char"] + } + ], + "outputs": + [ + { + "name":"ME", + "type":["MException"] + } + ] + }, + "mlapptools.decodeDijitRegistryResult": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"verboseFlag", + "kind":"optional", + "purpose":"Log printing setting", + "type":["boolean", "scalar"] + } + ], + "outputs": + [ + { + "name":"widgets", + "type":["struct"] + } + ] + }, + "mlapptools.emptyStructWithFields": + { + "inputs": + [ + { + "name":"fields", + "kind":"required", + "purpose":"List of IDs", + "type":["cell"] + } + ], + "outputs": + [ + { + "name":"eStruct", + "type":["struct"] + } + ] + }, + "mlapptools.establishIdentities": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + } + ], + "outputs": + [ + { + "name":"widgetID", + "type":["WidgetID"] + } + ] + }, + "mlapptools.getDataTag": + { + "inputs": + [ + { + "name":"hUIElement", + "kind":"required", + "purpose":"Web component handle", + "type":["matlab.ui.control.WebComponent"] + } + ], + "outputs": + [ + { + "name":"dataTag", + "type":["char"] + } + ] + }, + "mlapptools.figFromWebwindow": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + } + ], + "outputs": + [ + { + "name":"hUIFig", + "type":["matlab.ui.Figure"] + } + ] + }, + "mlapptools.getWidgetID": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"dataTag", + "kind":"required", + "purpose":"HTML node's data_tag", + "type":["char"] + } + ], + "outputs": + [ + { + "name":"widgetID", + "type":["WidgetID"] + } + ] + }, + "mlapptools.getWidgetIDFromDijit": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"dataTag", + "kind":"required", + "purpose":"HTML node's data_tag", + "type":["char"] + } + ], + "outputs": + [ + { + "name":"widgetID", + "type":["WidgetID"] + } + ] + }, + "mlapptools.getTimeout": + { + "inputs": + [ + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + } + ], + "outputs": + [ + { + "name":"to", + "type":["numeric"] + } + ] + }, + "mlapptools.isUIFigure": + { + "inputs": + [ + { + "name":"hFigList", + "kind":"required", + "purpose":"Suspected UIFigure handles", + "type":["matlab.ui.Figure"] + } + ], + "outputs": + [ + { + "name":"tf", + "type":["logical"] + } + ] + }, + "mlapptools.toggleWarnings": + { + "inputs": + [ + { + "name":"toggleStr", + "kind":"required", + "purpose":"New warning setting", + "type":["char", "choices={'on','off'}"] + } + ], + "outputs": + [ + { + "name":"oldState", + "type":["struct", "vector"] + } + ] + }, + "mlapptools.unifyStructs": + { + "inputs": + [ + { + "name":"cellOfStructs", + "kind":"required", + "purpose":"Structs to unify", + "type":["cell"] + } + ], + "outputs": + [ + { + "name":"uStruct", + "type":["struct"] + } + ] + }, + "mlapptools.validateAlignmentStr": + { + "inputs": + [ + { + "name":"alignment", + "kind":"required", + "purpose":"CSS alignment", + "type":["char"] + } + ] + }, + "mlapptools.validateCSScolor": + { + "inputs": + [ + { + "name":"color", + "kind":"required", + "purpose":"CSS color", + "type":["char"] + } + ], + "outputs": + [ + { + "name":"color", + "purpose":"transformed color", + "type":["char"] + } + ] + }, + "mlapptools.validateFontWeight": + { + "inputs": + [ + { + "name":"weight", + "kind":"required", + "purpose":"CSS font weight", + "type":["char"] + } + ], + "outputs": + [ + { + "name":"weight", + "purpose":"transformed weight", + "type":["char"] + } + ] + }, + "mlapptools.waitTillFigureLoaded": + { + "inputs": + [ + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + } + ] + }, + "mlapptools.waitTillWebwindowLoaded": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"hUIFig", + "kind":"required", + "purpose":"UIFigure handle", + "type":["matlab.ui.Figure"] + } + ] } } \ No newline at end of file diff --git a/mlapptools.m b/mlapptools.m index cd7dda6..39f6049 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -218,7 +218,7 @@ function fontWeight(hUIElement, weight) hWin.executeJS('W = undefined'); end % getParentNodeID - function [widgetID] = getTableCellID(hUITable, r, c) + function [widgetIDs] = getTableCellID(hUITable, r, c) % This method returns one or more ID objects, corresponding to specific cells in a % uitable, as defined by the cells' row and column indices. %% Constants: @@ -235,14 +235,14 @@ function fontWeight(hUIElement, weight) %% Computing the offset % If there's more than one uitable in the figure, IDs are assigned % consecutively. For example, in the case of a 3x3 and 4x4 arrays, - % where the smaller table was created first, IDs will assigned as follows: + % where the smaller table was created first, IDs are assigned as follows: % % [ 0 1 2 [ 9 10 11 12 % 3 4 5 13 14 15 16 % 6 7 8] 17 18 19 20 % 21 22 23 24] % - % ... which is why if want to change the 2nd table we need to offset the + % ... which is why if we want to change the 2nd table we need to offset the % IDs by the total amount of elements in all preceding arrays, which is 9. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Get siblings (flipping to negate that newer children appear first) @@ -256,14 +256,14 @@ function fontWeight(hUIElement, weight) idx = sub2ind(sz, c, r) + offset - 1; % -1 because JS IDs are 0-based idc = strcat(CELL_ID_PREFIX, num2str(idx(:))); % Preallocation: - widgetID(nR,1) = WidgetID; + widgetIDs(nR,1) = WidgetID; % Get the window handle so we could execute some JS commands: hWin = mlapptools.getWebWindow(hUITable); % ID array population: for indI = 1:numel(idx) jsCommand = sprintf('dojo.byId(%s).childNodes[0].id', idc{indI}); textFieldID = hWin.executeJS(jsCommand); - widgetID(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, textFieldID(2:end-1) ); + widgetIDs(indI) = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, textFieldID(2:end-1) ); end end % getTableCellID @@ -290,12 +290,17 @@ function fontWeight(hUIElement, weight) widgetID = WidgetID('data-tag', mlapptools.getDataTag(hUIElement)); case 'uitable' TAB_PREFIX = "mgg_"; - % uitables are inconsistent with other elements, their id always starts with - % "mgg_". So we list all widgets and select the "table-suspects" among them. - % Note: the listing is not really necessary, as it is possible to search for - % nodes having a property that starts with a certain string: E[foo^="bar"] - % web(['http://dojotoolkit.org/reference-guide/1.10/dojo/query.html',... - % '#additional-selectors-supported-by-lite-engine'], '-browser'); + % Notes: + % 1) uitables are inconsistent with other elements, their id always starts with + % "mgg_". So we list all widgets and select the "table-suspects" among them. + % 2) It's probably more useful to style the individual cells rather than the + % table itself. See: getTableCellID + % 3) The listing is not necessary, as it is possible to search for nodes + % having a property that starts with a certain string: E[foo^="bar"] + %{ + web(['http://dojotoolkit.org/reference-guide/1.10/dojo/query.html',... + '#additional-selectors-supported-by-lite-engine'], '-browser'); + %} [~,tmp] = mlapptools.getWidgetList( ancestor(hUIElement,'figure') ); widgetID = arrayfun(@(x)WidgetID(mlapptools.DEF_ID_ATTRIBUTE, x), ... string(tmp.id(contains(tmp.id, TAB_PREFIX)))); @@ -461,90 +466,90 @@ function textAlign(hUIElement, alignment) function unlockUIFig(hUIFig) % This method allows the uifigure to be opened in an external browser, % as was possible before R2017b. - assert(checkCert(), 'Certificate not imported; cannot proceed.'); + assert(checkCert(), 'Certificate not imported; cannot proceed.'); if verLessThan('matlab','9.3') % Do nothing, since this is not required pre-R2017b. else struct(hUIFig).Controller.ProxyView.PeerNode.setProperty('hostType','""'); end - - function tf = checkCert() - % This tools works on the OS-level, and was tested on Win 7 & 10. - % - % With certain browsers it might not be required/helpful as noted in - % https://askubuntu.com/questions/73287/#comment1533817_94861 : - % Note that Chromium and Firefox do not use the system CA certificates, - % so require separate instructions. - % - In Chromium, visit chrome://settings/certificates, click Authorities, - % then click import, and select your .pem. - % - In Firefox, visit about:preferences#privacy, click Certificates, - % View Certificates, Authorities, then click Import and select your .pem. - SUCCESS_CODE = 0; - CL = connector.getCertificateLocation(); % certificate location; - if isempty(CL), CL = fullfile(prefdir, 'thisMatlab.pem'); end - %% Test if certificate is already accepted: - switch true + + function tf = checkCert() + % This tools works on the OS-level, and was tested on Win 7 & 10. + % + % With certain browsers it might not be required/helpful as noted in + % https://askubuntu.com/questions/73287/#comment1533817_94861 : + % Note that Chromium and Firefox do not use the system CA certificates, + % so require separate instructions. + % - In Chromium, visit chrome://settings/certificates, click Authorities, + % then click import, and select your .pem. + % - In Firefox, visit about:preferences#privacy, click Certificates, + % View Certificates, Authorities, then click Import and select your .pem. + SUCCESS_CODE = 0; + CL = connector.getCertificateLocation(); % certificate location; + if isempty(CL), CL = fullfile(prefdir, 'thisMatlab.pem'); end + %% Test if certificate is already accepted: + switch true + case ispc + [s,c] = system('certutil -verifystore -user "Root" localhost'); + case isunix + [s,c] = system(['openssl crl2pkcs7 -nocrl -certfile '... + '/etc/ssl/certs/ca-certificates.crt '... + '| openssl pkcs7 -print_certs -noout '... + '| grep ''^issuer=/C=US/O=company/CN=localhost/OU=engineering''']); + case ismac + [s,c] = system('security find-certificate -c "localhost"'); + end + isAccepted = s == SUCCESS_CODE; + + %% Try to import certificate: + if isAccepted + wasImported = false; + else + reply = questdlg('Certificate not found. Would you like to import it?',... + 'Import "localhost" certificate','Yes','No','Yes'); + if strcmp(reply,'Yes'), switch true %#ok case ispc - [s,c] = system('certutil -verifystore -user "Root" localhost'); + [s,c] = system(['certutil -addstore -user "Root" ' CL]); + % %APPDATA%\MathWorks\MATLAB\R20##x\thisMatlab.pem case isunix - [s,c] = system(['openssl crl2pkcs7 -nocrl -certfile '... - '/etc/ssl/certs/ca-certificates.crt '... - '| openssl pkcs7 -print_certs -noout '... - '| grep ''^issuer=/C=US/O=company/CN=localhost/OU=engineering''']); - case ismac - [s,c] = system('security find-certificate -c "localhost"'); - end - isAccepted = s == SUCCESS_CODE; - - %% Try to import certificate: - if isAccepted - wasImported = false; - else - reply = questdlg('Certificate not found. Would you like to import it?',... - 'Import "localhost" certificate','Yes','No','Yes'); - if strcmp(reply,'Yes'), switch true %#ok - case ispc - [s,c] = system(['certutil -addstore -user "Root" ' CL]); - % %APPDATA%\MathWorks\MATLAB\R20##x\thisMatlab.pem - case isunix - [s,c] = system(['sudo cp ' CL ... - ' /usr/local/share/ca-certificates/localhost-matlab.crt && ',... - 'sudo update-ca-certificates']); - % ~/.matlab/thisMatlab.pem - case ismac % https://apple.stackexchange.com/a/80625 - [s,c] = system(['security add-trusted-cert -d -r trustRoot -p ssl -k ' ... - '"$HOME/Library/Keychains/login.keychain" ' CL]); - % ~/Library/Application\ Support/MathWorks/MATLAB/R20##x/thisMatlab.pem - end % switch - wasImported = s == SUCCESS_CODE; - else - warning('Certificate import cancelled by user!'); - wasImported = false; - end - end - %% Report result - tf = isAccepted || wasImported; - if wasImported - fprintf(1, '\n%s\n%s\n%s\n',... - ['Certificate import successful! You should now be '... - 'able to navigate to the webwindow URL in your browser.'],... - ['If the figure is still blank, recreate it and navigate '... - 'to the new URL.'],... - ['Also, if you have a script blocking addon (e.g. NoScript), '... - 'be sure to whitelist "localhost".']); - elseif ~isAccepted % && ~wasImported (implicitly) - disp(c); - fprintf(1, '\n%s\n%s\n\t%s\n\t%s\n%s\n',... - 'Either certificate presence cannot be determined, or the import failed.',... - 'If you''re using Chromium or Firefox you can follow these instructions:',... - ['- In Chromium, visit chrome://settings > (Show advanced) > '... - 'Manage HTTP/SSL certificates > Trusted Root Certification Authorities Tab'... - ' > Import, and select your .pem.'],... - ['- In Firefox, visit about:preferences#privacy, click Certificates > ',... - 'View Certificates > Authorities > Import, and select your .pem.'],... - ['The certificate is found here: ' CL ]); - end - end % checkCert + [s,c] = system(['sudo cp ' CL ... + ' /usr/local/share/ca-certificates/localhost-matlab.crt && ',... + 'sudo update-ca-certificates']); + % ~/.matlab/thisMatlab.pem + case ismac % https://apple.stackexchange.com/a/80625 + [s,c] = system(['security add-trusted-cert -d -r trustRoot -p ssl -k ' ... + '"$HOME/Library/Keychains/login.keychain" ' CL]); + % ~/Library/Application\ Support/MathWorks/MATLAB/R20##x/thisMatlab.pem + end % switch + wasImported = s == SUCCESS_CODE; + else + warning('Certificate import cancelled by user!'); + wasImported = false; + end + end + %% Report result + tf = isAccepted || wasImported; + if wasImported + fprintf(1, '\n%s\n%s\n%s\n',... + ['Certificate import successful! You should now be '... + 'able to navigate to the webwindow URL in your browser.'],... + ['If the figure is still blank, recreate it and navigate '... + 'to the new URL.'],... + ['Also, if you have a script blocking addon (e.g. NoScript), '... + 'be sure to whitelist "localhost".']); + elseif ~isAccepted % && ~wasImported (implicitly) + disp(c); + fprintf(1, '\n%s\n%s\n\t%s\n\t%s\n%s\n',... + 'Either certificate presence cannot be determined, or the import failed.',... + 'If you''re using Chromium or Firefox you can follow these instructions:',... + ['- In Chromium, visit chrome://settings > (Show advanced) > '... + 'Manage HTTP/SSL certificates > Trusted Root Certification Authorities Tab'... + ' > Import, and select your .pem.'],... + ['- In Firefox, visit about:preferences#privacy, click Certificates > ',... + 'View Certificates > Authorities > Import, and select your .pem.'],... + ['The certificate is found here: ' CL ]); + end + end % checkCert end % unlockUIFig function hWin = waitForFigureReady(hUIFig) @@ -658,9 +663,9 @@ function unlockUIFig(hUIFig) end end % establishIdentity - function [data_tag] = getDataTag(hUIElement) + function [dataTag] = getDataTag(hUIElement) warnState = mlapptools.toggleWarnings('off'); - data_tag = char( struct(hUIElement).Controller.ProxyView.PeerNode.getId() ); + dataTag = char( struct(hUIElement).Controller.ProxyView.PeerNode.getId() ); warning(warnState); end % getDataTag @@ -685,7 +690,7 @@ function unlockUIFig(hUIFig) end % figFromWebwindow function [widgetID] = getWidgetID(hWin, dataTag) - % This method returns a structure containing some uniquely-identifying information + % This method returns an object containing some uniquely-identifying information % about a DOM node. widgetquerystr = sprintf(... 'dojo.getAttr(dojo.query("[data-tag^=''%s''] > div")[0], "widgetid")', dataTag); @@ -733,17 +738,18 @@ function unlockUIFig(hUIFig) end % getWidgetIDFromDijit function to = getTimeout(hUIFig) - if isempty(hUIFig) || ~isa(hUIFig, 'matlab.ui.Figure') - to = mlapptools.QUERY_TIMEOUT; - else - to = getappdata(hUIFig, mlapptools.TAG_TIMEOUT); - if isempty(to), to = mlapptools.QUERY_TIMEOUT; end - end + if isempty(hUIFig) || ~isa(hUIFig, 'matlab.ui.Figure') + to = mlapptools.QUERY_TIMEOUT; + else + to = getappdata(hUIFig, mlapptools.TAG_TIMEOUT); + if isempty(to), to = mlapptools.QUERY_TIMEOUT; end + end end % getTimeout function tf = isUIFigure(hFigList) tf = arrayfun(@(x)isa(x,'matlab.ui.Figure') && ... isstruct(struct(x).ControllerInfo), hFigList); + % See also: matlab.ui.internal.isUIFigure() end % isUIFigure function oldState = toggleWarnings(toggleStr) From c3cb07a9ce243b6cfd83551fac9a259d58da03e0 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Tue, 9 Oct 2018 16:54:44 +0300 Subject: [PATCH 14/18] Fixed readme entry related to `setTimeout` Also: - Added a warning. - Slightly modified some warning text. --- README.md | 2 +- mlapptools.m | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f0e19e1..aa3c823 100644 --- a/README.md +++ b/README.md @@ -338,7 +338,7 @@ mlapptools.setStyle(myGUI.TextArea, 'background-image',... ----------------- -#### *mlapptools*.**setTimeout**(*hUIFig*) +#### *mlapptools*.**setTimeout**(*hUIFig*, *newTimeoutInSec*) ##### Description diff --git a/mlapptools.m b/mlapptools.m index 39f6049..a3c4d1c 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -7,7 +7,7 @@ % % aboutJSLibs - Return version information about certain JS libraries. % addClasses - Add specified CSS classes to one or more DOM nodes. - % addCssToHead - Inject inline CSS into the section of the figure's HTML. + % addToHead - Inject inline CSS/JS into the section of the figure's HTML. % fontColor - Modify font color. % fontWeight - Modify font weight. % getHTML - Return the full HTML code of a uifigure. @@ -104,11 +104,11 @@ function addToHead(hWin, nodeText) if isfile(nodeText) warning(['Files are not supported at this time.'... ' Please provide a "stringified" input instead.'... - '\nInput can be "" or "".'],[]); + '\nInput can be "" or "".'],[]); elseif ~isempty(regexpi(nodeText,'http(s)?://')) warning(['Remote files are not supported at this time.',... ' Please provide a "stringified" input instead.'... - '\nInput can be "" or "".'],[]); + '\nInput can be "" or "".'],[]); end % Inject the nodeText: @@ -766,7 +766,7 @@ function unlockUIFig(hUIFig) warning('off',OJF); warning('off',SOO); otherwise - % Do nothing + warning(['Unrecognized option "' toggleStr '". Please use either "on" or "off".']); end end % toggleWarnings From 6068af3b24f35c74813afc39c1b66c04ee9390cf Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 24 Mar 2019 18:20:07 +0200 Subject: [PATCH 15/18] Added a utility function for getting node descendants of a certain type - Updated functionSignatures.json accordingly. - TODO: mention in main readme. --- functionSignatures.json | 31 +++++++++++++++++++++++++++++++ mlapptools.m | 20 ++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/functionSignatures.json b/functionSignatures.json index 2018079..b05d3b9 100644 --- a/functionSignatures.json +++ b/functionSignatures.json @@ -529,6 +529,37 @@ } ] }, + "mlapptools.getDecendentOfType": + { + "inputs": + [ + { + "name":"hWin", + "kind":"required", + "purpose":"webWindow handle", + "type":["matlab.internal.webwindow"] + }, + { + "name":"ancestorDataTag", + "kind":"required", + "purpose":"HTML node's data_tag", + "type":["char"] + }, + { + "name":"descendentType", + "kind":"required", + "purpose":"Target descendent's HTML tag", + "type":["char"] + } + ], + "outputs": + [ + { + "name":"widgetID", + "type":["WidgetID"] + } + ] + }, "mlapptools.figFromWebwindow": { "inputs": diff --git a/mlapptools.m b/mlapptools.m index a3c4d1c..d0193f5 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -669,6 +669,26 @@ function unlockUIFig(hUIFig) warning(warnState); end % getDataTag + function [widgetID] = getDecendentOfType(hWin, ancestorDataTag, descendentType) + % This method returns a node's first descendent of a specified . + % See also: + % https://dojotoolkit.org/reference-guide/1.10/dojo/query.html#standard-css2-selectors + widgetquerystr = sprintf(... + 'dojo.getAttr(dojo.query("[data-tag^=''%s''] %s")[0], "%s")', ... + ancestorDataTag, descendentType, mlapptools.DEF_ID_ATTRIBUTE); + try % should work for most UI objects + ID = hWin.executeJS(widgetquerystr); + if ~strcmpi(ID,'null') + widgetID = WidgetID(mlapptools.DEF_ID_ATTRIBUTE, ID(2:end-1)); + return + end + catch + warning(['Error encountered while obtaining a descendent of ', ancestorDataTag]); + end + % If the JS command failed, or no descendent found, return an empty WidgetID object. + widgetID = WidgetID(); + end % getDecendentOfType + function hFig = figFromWebwindow(hWin) % Using this method is discouraged as it's relatively computation-intensive. % Since the figure handle is not a property of the webwindow or its children From 5a52ddd32b83f8c5b10b327afcc1c236efcfd156 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Sun, 24 Mar 2019 18:22:51 +0200 Subject: [PATCH 16/18] Added handling for uiaxes in getWebElements A canvas is now returned for a uiaxes input, along with a warning. --- mlapptools.m | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mlapptools.m b/mlapptools.m index d0193f5..121a2ec 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -304,6 +304,15 @@ function fontWeight(hUIElement, weight) [~,tmp] = mlapptools.getWidgetList( ancestor(hUIElement,'figure') ); widgetID = arrayfun(@(x)WidgetID(mlapptools.DEF_ID_ATTRIBUTE, x), ... string(tmp.id(contains(tmp.id, TAB_PREFIX)))); + case 'axes' + % For uiaxes we return the object. This canvas has a context + % of type "webgl". + % See also: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API + warning(['UIAxes object detected. Returning the innermost element. '... + 'Be advised that mlapptools cannot modify this element, which '... + 'instead requires using WebGL commands via `hWin.executeJS(...)`.']); + widgetID = mlapptools.getDecendentOfType( ... + hWin, mlapptools.getDataTag(hUIElement), 'canvas'); otherwise % default: widgetID = mlapptools.getWidgetID(hWin, mlapptools.getDataTag(hUIElement)); end From 76d8e709615e90dcbd372ddb753059158a8b2cd9 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Mon, 25 Mar 2019 16:29:27 +0200 Subject: [PATCH 17/18] Adaptation for R2019a in getWebElements() for uiaxes inputs In R2019a a node is returned, as opposed to earlier versions, in which would be returned. --- mlapptools.m | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mlapptools.m b/mlapptools.m index 121a2ec..7aee2e3 100644 --- a/mlapptools.m +++ b/mlapptools.m @@ -305,14 +305,19 @@ function fontWeight(hUIElement, weight) widgetID = arrayfun(@(x)WidgetID(mlapptools.DEF_ID_ATTRIBUTE, x), ... string(tmp.id(contains(tmp.id, TAB_PREFIX)))); case 'axes' - % For uiaxes we return the object. This canvas has a context - % of type "webgl". - % See also: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API - warning(['UIAxes object detected. Returning the innermost element. '... + switch subsref(ver('matlab'), substruct('.','Version')) + case {'9.6'} % R2019a + descendType = 'img'; + otherwise % R2016a-R2018b + descendType = 'canvas'; + % This canvas has a context of type "webgl". + % See also: https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API + warning(['UIAxes object detected. Returning the innermost element. '... 'Be advised that mlapptools cannot modify this element, which '... - 'instead requires using WebGL commands via `hWin.executeJS(...)`.']); + 'instead requires using WebGL commands via `hWin.executeJS(...)`.']); + end widgetID = mlapptools.getDecendentOfType( ... - hWin, mlapptools.getDataTag(hUIElement), 'canvas'); + hWin, mlapptools.getDataTag(hUIElement), descendType); otherwise % default: widgetID = mlapptools.getWidgetID(hWin, mlapptools.getDataTag(hUIElement)); end From ae586ae37c81d45fbc1a12e5037fe9e0b3957d26 Mon Sep 17 00:00:00 2001 From: Dev-iL Date: Mon, 25 Mar 2019 16:30:58 +0200 Subject: [PATCH 18/18] Added a note about support for uifigure "unlocking" in various releases --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa3c823..93b60a2 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,8 @@ mlapptools.textAlign(myGUI.TextArea, 'right'); This method modifies the properties of the UIFigure controller such that opening the UIFigure in a browser (using the webwindow URL) becomes possible. This method has no effect on MATLAB releases before R2017b (where no such restriction exists in the first place). +_NOTE:_ this method works as expected in releases ≤R2018a. In newer releases, the unlocked figure can only be viewed in the built-in web browser using `web(hWW.URL,'-new')`. This way one can view the source code of the webpage (useful to examine the DOM's hierarchy etc.), but not modify it (by editing the nodes manually or issuing console commands). + ##### Examples Using the demo GUI generated by `./Demo/DOMdemoGUI.m` @@ -408,7 +410,8 @@ web(hWW.URL,'-browser'); pause(3); mlapptools.unlockUIFig(myGUI.UIFigure); % Attempt to open again: -web(hWW.URL,'-browser'); +web(hWW.URL,'-browser'); % Works on R2016a-R2018a +web(hWW.URL,'-new'); % Works on all releases (probably) ``` The 1st browser window should be empty, whereas the 2nd should correctly show the contents of the UIFigure.