diff --git a/docs/BlocklyClass.html b/docs/BlocklyClass.html new file mode 100644 index 000000000..990ea1fef --- /dev/null +++ b/docs/BlocklyClass.html @@ -0,0 +1,171 @@ + + + + + JSDoc: Class: BlocklyClass + + + + + + + + + + +
+ +

Class: BlocklyClass

+ + + + + + +
+ +
+ +

BlocklyClass()

+ +
Replacement for singleton Blockly object. This defines only the methods and +values used by block creation code.
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new BlocklyClass()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/GuiEnvironment.html b/docs/GuiEnvironment.html new file mode 100644 index 000000000..168ee9346 --- /dev/null +++ b/docs/GuiEnvironment.html @@ -0,0 +1,833 @@ + + + + + JSDoc: Class: GuiEnvironment + + + + + + + + + + +
+ +

Class: GuiEnvironment

+ + + + + + +
+ +
+ +

GuiEnvironment()

+ +
Class to handle connections to the outside world. (A different class with +the same methods is used for testing purposes.)
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new GuiEnvironment()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

displayError(error)

+ + + + + + +
+ Display an error. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +string + + + + The message to display.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

displayPlot(spec)

+ + + + + + +
+ Display a plot. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
spec + + +Object + + + + Vega-Lite spec for plot with data filled in.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

displayTable(table)

+ + + + + + +
+ Display a table (as HTML). +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
table + + +Object + + + + JSON array of uniform objects.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

getCode() → {string}

+ + + + + + +
+ Get the code to run. +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ The code to run. +
+ + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

readCSV(url)

+ + + + + + +
+ Read CSV from a URL and parse to create TidyBlocks data frame. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url + + +string + + + + URL to read from.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/MockBlock.html b/docs/MockBlock.html index 309e7d748..f4e52afa5 100644 --- a/docs/MockBlock.html +++ b/docs/MockBlock.html @@ -93,7 +93,7 @@

new MockBloc
Source:
@@ -155,13 +155,13 @@

new MockBloc
diff --git a/docs/TestEnvironment.html b/docs/TestEnvironment.html new file mode 100644 index 000000000..72642bb1f --- /dev/null +++ b/docs/TestEnvironment.html @@ -0,0 +1,843 @@ + + + + + JSDoc: Class: TestEnvironment + + + + + + + + + + +
+ +

Class: TestEnvironment

+ + + + + + +
+ +
+ +

TestEnvironment()

+ +
Environment for testing. (Replaces the one in the GUI.)
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new TestEnvironment()

+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

displayError(error)

+ + + + + + +
+ Display an error (record for testing purposes). +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +string + + + + message to record.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

displayPlot(spec)

+ + + + + + +
+ "Display" a plot (record for testing purposes). +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
spec + + +Object + + + + Vega-Lite spec for plot.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

displayTable(data)

+ + + + + + +
+ "Display" a table (record for testing purposes). +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Object + + + + data to record.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

getCode() → {string}

+ + + + + + +
+ Get the code to run. +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ The code to run. +
+ + + +
+
+ Type +
+
+ +string + + +
+
+ + + + + + + + + + + + + +

readCSV(url)

+ + + + + + +
+ Read a CSV file. Defined here to (a) load local CSV and (b) be in scope for +'eval' of generated code. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url + + +string + + + + URL of data.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ dataframe containing that data. +
+ + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/TidyBlocksDataFrame.html b/docs/TidyBlocksDataFrame.html index 03a6055a3..5f3b2dc6d 100644 --- a/docs/TidyBlocksDataFrame.html +++ b/docs/TidyBlocksDataFrame.html @@ -146,7 +146,7 @@

Parameters:
Source:
@@ -303,7 +303,7 @@
Parameters:
Source:
@@ -450,7 +450,7 @@
Parameters:
Source:
@@ -609,7 +609,7 @@
Parameters:
Source:
@@ -655,7 +655,7 @@
Returns:
-

hasColumn(name) → {Boolean}

+

hasColumns(names) → {Boolean}

@@ -663,7 +663,7 @@

hasColumn - Test whether the dataframe has the specified column. + Test whether the dataframe has the specified columns. @@ -699,13 +699,13 @@
Parameters:
- name + names -string +Array.<string> @@ -715,7 +715,7 @@
Parameters:
- Name of column to check for. + Names of column to check for. @@ -756,7 +756,7 @@
Parameters:
Source:
@@ -785,7 +785,7 @@
Returns:
- Is column present? + Are columns present?
@@ -1007,7 +1007,7 @@
Parameters:
Source:
@@ -1177,7 +1177,7 @@
Parameters:
Source:
@@ -1348,7 +1348,7 @@
Parameters:
Source:
@@ -1384,7 +1384,7 @@
Parameters:
-

plot(tableFxn, plotFxn, spec)

+

plot(environment, spec)

@@ -1430,36 +1430,13 @@
Parameters:
- tableFxn + environment -function - - - - - - - - - - Callback to display table as table. - - - - - - - plotFxn - - - - - -function +object @@ -1469,7 +1446,7 @@
Parameters:
- Callback to display table graphically. + Connection to the outside world. @@ -1533,7 +1510,7 @@
Parameters:
Source:
@@ -1573,94 +1550,6 @@
Returns:
- - - - - - -

reverse()

- - - - - - -
- Reverse the order of rows. -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - @@ -1768,7 +1657,7 @@
Parameters:
Source:
@@ -1915,7 +1804,7 @@
Parameters:
Source:
@@ -2085,7 +1974,7 @@
Parameters:
Source:
@@ -2232,7 +2121,7 @@
Parameters:
Source:
@@ -2330,7 +2219,7 @@

toStringSource:
@@ -2418,7 +2307,7 @@

ungroupSource:
@@ -2474,13 +2363,13 @@
Returns:

- Documentation generated by JSDoc 3.6.3 on Thu Sep 05 2019 20:42:06 GMT-0400 (Eastern Daylight Time) + Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:04 GMT-0400 (Eastern Daylight Time)
diff --git a/docs/TidyBlocksManagerClass.html b/docs/TidyBlocksManagerClass.html index c8a43f7b3..39044290a 100644 --- a/docs/TidyBlocksManagerClass.html +++ b/docs/TidyBlocksManagerClass.html @@ -97,7 +97,7 @@

Source:
@@ -254,7 +254,7 @@

Parameters:
Source:
@@ -391,7 +391,7 @@
Parameters:
Source:
@@ -502,7 +502,7 @@

getNumBlo
Source:
@@ -639,7 +639,7 @@

Parameters:
Source:
@@ -810,7 +810,7 @@
Parameters:
Source:
@@ -993,7 +993,7 @@
Parameters:
Source:
@@ -1081,7 +1081,7 @@

resetSource:
@@ -1117,7 +1117,7 @@

resetrun(getCode, displayTable, displayPlot, displayError, readCSV)

+

run(environment)

@@ -1162,105 +1162,13 @@
Parameters:
- getCode + environment -function - - - - - - - - - - How to get the code to run. - - - - - - - displayTable - - - - - -function - - - - - - - - - - How to display a table (used in 'eval'). - - - - - - - displayPlot - - - - - -function - - - - - - - - - - How to display a plot (used in 'eval'). - - - - - - - displayError - - - - - -function - - - - - - - - - - How to display an error (used in 'eval' and here). - - - - - - - readCSV - - - - - -function +object @@ -1270,7 +1178,7 @@
Parameters:
- How to read a CSV file (used in 'eval'). + How to interact with the outside world. @@ -1311,7 +1219,7 @@
Parameters:
Source:
@@ -1399,7 +1307,7 @@

toStringSource:
@@ -1445,13 +1353,13 @@

toString
- Documentation generated by JSDoc 3.6.3 on Thu Sep 05 2019 20:42:06 GMT-0400 (Eastern Daylight Time) + Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:04 GMT-0400 (Eastern Daylight Time)
diff --git a/docs/global.html b/docs/global.html index 8796f0257..16a646eaa 100644 --- a/docs/global.html +++ b/docs/global.html @@ -98,14 +98,13 @@

Members

-

(constant) Blockly

+

(constant) TIDYBLOCKS_START

- Replacement for singleton Blockly object. This defines only the methods and -values used by block creation code. + Prefix and suffix for well-formed pipelines.
@@ -143,7 +142,7 @@

(constant) Blo
Source:
@@ -161,13 +160,13 @@

(constant) Blo -

(constant) Result

+

(constant) TidyBlocksManager

- Record results for testing purposes. + Singleton instance of manager.
@@ -205,7 +204,7 @@

(constant) Resu
Source:
@@ -222,14 +221,26 @@

(constant) Resu + + + +

Methods

+ + -

(constant) TERMINATOR

+ + + +

assert_hasKey(actual, required, message)

+ + +
- Terminator for well-formed pipelines. + Assert that an object has a key.
@@ -238,62 +249,101 @@

(constant) -
- +
Parameters:
- + + + + + + - + - + - + - + + + + - + + + + - + - -
Source:
-
- + - + - + + - + + + + + + - -

(constant) TidyBlocksManager

+ + + + + + + + + + + + + + + + + + + +
NameTypeDescription
actual + + +string - - + + Object being examined.
required + + +string + + Key that must be present.
message + + +string -
- Singleton instance of manager. -
+ +
Error message.
@@ -329,7 +379,7 @@

(constant)
Source:
@@ -345,11 +395,19 @@

(constant) - - - -

Methods

+ + + + + + + + + + + + @@ -357,7 +415,7 @@

Methods

-

assert_hasKey(actual, required, message)

+

assert_includes(actual, required, message)

@@ -365,7 +423,7 @@

assert_h
- Assert that an object has a key. + Assert that one string contains another.
@@ -417,7 +475,7 @@

Parameters:
- Object being examined. + String being examined. @@ -440,7 +498,7 @@
Parameters:
- Key that must be present. + String to look for. @@ -504,7 +562,7 @@
Parameters:
Source:
@@ -540,7 +598,7 @@
Parameters:
-

assert_includes(actual, required, message)

+

assert_match(actual, required, message)

@@ -548,7 +606,7 @@

assert
- Assert that one string contains another. + Assert that a string matches a regular expression.
@@ -613,7 +671,7 @@

Parameters:
-string +regexp @@ -623,7 +681,7 @@
Parameters:
- String to look for. + Pattern to look for. @@ -687,7 +745,7 @@
Parameters:
Source:
@@ -870,7 +928,7 @@
Parameters:
Source:
@@ -1002,7 +1060,7 @@
Parameters:
Source:
@@ -1174,7 +1232,7 @@
Parameters:
Source:
@@ -1345,7 +1403,7 @@
Parameters:
Source:
@@ -1443,7 +1501,7 @@

deleteBloc
Source:
@@ -1479,7 +1537,7 @@

deleteBloc -

displayError(error)

+

displayTab()

@@ -1487,7 +1545,7 @@

displayEr
- Display an error (record for testing purposes). + Toggle between tabs for dataframe and error pane
@@ -1498,55 +1556,6 @@

displayEr -

Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
error - - -string - - - - message to record.
- - @@ -1580,7 +1589,7 @@
Parameters:
Source:
@@ -1616,7 +1625,7 @@
Parameters:
-

displayError(error)

+

evalCode(code)

@@ -1624,7 +1633,7 @@

displayEr
- Callback fro displaying an error online. + Run code block.
@@ -1660,7 +1669,7 @@

Parameters:
- error + code @@ -1676,7 +1685,7 @@
Parameters:
- The message to display. + code to evaluate. @@ -1717,7 +1726,7 @@
Parameters:
Source:
@@ -1742,6 +1751,16 @@
Parameters:
+
Returns:
+ + +
+ environment (including eval'd code). +
+ + + + @@ -1753,7 +1772,7 @@
Parameters:
-

displayPlot(spec)

+

findLineByLeastSquares(values_x, values_y) → {Array.<number>}

@@ -1761,7 +1780,7 @@

displayPlo
- Callback for displaying a plot. + Find linear model for plotting.
@@ -1797,13 +1816,36 @@

Parameters:
- spec + values_x -Object +Array.<number> + + + + + + + + + + X-axis values. + + + + + + + values_y + + + + + +Array.<number> @@ -1813,7 +1855,7 @@
Parameters:
- Vega-Lite spec for plot with data filled in. + Y-axis values. @@ -1854,7 +1896,7 @@
Parameters:
Source:
@@ -1879,6 +1921,28 @@
Parameters:
+
Returns:
+ + +
+ Slope and intercept. +
+ + + +
+
+ Type +
+
+ +Array.<number> + + +
+
+ + @@ -1890,7 +1954,7 @@
Parameters:
-

displayPlot(spec)

+

fixCode(code)

@@ -1898,7 +1962,7 @@

displayPlo
- "Display" a plot (record for testing purposes). + Fix up runnable code if it isn't properly terminated yet.
@@ -1934,13 +1998,13 @@

Parameters:
- spec + code -Object +string @@ -1950,7 +2014,7 @@
Parameters:
- Vega-Lite spec for plot. + Pipeline code to be terminated if necessary. @@ -1991,7 +2055,7 @@
Parameters:
Source:
@@ -2027,7 +2091,7 @@
Parameters:
-

displayTab()

+

generateCode(code)

@@ -2035,7 +2099,7 @@

displayTab<
- Toggle between tabs for dataframe and error pane + Assemble the code produced by blocks into a single string.
@@ -2046,6 +2110,61 @@

displayTab< +

Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
code + + +string +| + +Array.<string> +| + +number + + + + input
+ + @@ -2079,7 +2198,7 @@

displayTab<
Source:
@@ -2104,6 +2223,16 @@

displayTab< +

Returns:
+ + +
+ a single string +
+ + + + @@ -2115,7 +2244,7 @@

displayTab< -

displayTable(data)

+

generateCodePane()

@@ -2123,7 +2252,7 @@

displayTa
- "Display" a table (record for testing purposes). + Toggle between block input and text input panes.
@@ -2134,53 +2263,93 @@

displayTa -

Parameters:
+ + + + +
+ - - - - - - + - + - + - + - - - + - - - - - + - - + + + + + + + + + + + + + + + + + - - + - -
NameTypeDescription
data - - -Object + + + + + + + + + +
Source:
+
+ + + + + + + + - -
data to record.
+

initializeDisplay()

+ + + + + + +
+ Set the display property of the two input toggleable panes. +(Has to be done manually rather than in CSS because properties are being reset.) +
+ + + + + + + @@ -2216,7 +2385,7 @@
Parameters:
Source:
@@ -2252,7 +2421,7 @@
Parameters:
-

displayTable(table)

+

json2table(json)

@@ -2260,7 +2429,8 @@

displayTa
- Callback for displaying a table as HTML. + Create dynamic table from array from JSON with one table column per property. +Each object must have the same properties.
@@ -2296,13 +2466,13 @@

Parameters:
- table + json -Object +JSON @@ -2312,7 +2482,7 @@
Parameters:
- JSON array of uniform objects. + JSON object to convert to table. @@ -2353,7 +2523,7 @@
Parameters:
Source:
@@ -2389,7 +2559,7 @@
Parameters:
-

evalCode(code)

+

loadBlockFiles()

@@ -2397,7 +2567,8 @@

evalCode - Run code block. + Read 'index.html', find block files, and eval those. +Does _not_ read R files (for now). @@ -2408,55 +2579,6 @@

evalCodeParameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
code - - -string - - - - code to evaluate.
- - @@ -2490,7 +2612,7 @@
Parameters:
Source:
@@ -2515,16 +2637,6 @@
Parameters:
-
Returns:
- - -
- generated code (for checking). -
- - - - @@ -2536,7 +2648,7 @@
Returns:
-

findLineByLeastSquares(values_x, values_y) → {Array.<number>}

+

loadCode(fileList)

@@ -2544,7 +2656,8 @@

- Find linear model for plotting. + Load saved code. +Depends on the global TidyBlocksWorkspace variable. @@ -2580,13 +2693,13 @@

Parameters:
- values_x + fileList -Array.<number> +Array.<string> @@ -2596,35 +2709,12 @@
Parameters:
- X-axis values. + List of files (only first element is valid). - - - - values_y - - - - - -Array.<number> - - - - - - - - - - Y-axis values. - - - - - + + @@ -2660,7 +2750,7 @@
Parameters:
Source:
@@ -2685,28 +2775,6 @@
Parameters:
-
Returns:
- - -
- Slope and intercept. -
- - - -
-
- Type -
-
- -Array.<number> - - -
-
- - @@ -2718,7 +2786,7 @@
Returns:
-

fixCode(code)

+

makeBlock(blockName, settings)

@@ -2726,7 +2794,9 @@

fixCode - Fix up runnable code if it isn't properly terminated yet. + Make a block by name. If the construction function returns a string, that's +what we want; otherwise, it's a two-element list with the desired text and +the order, so we return the first element. @@ -2762,7 +2832,7 @@
Parameters:
- code + blockName @@ -2778,7 +2848,30 @@
Parameters:
- Pipeline code to be terminated if necessary. + must match string name of block. + + + + + + + settings + + + + + +Object + + + + + + + + + + settings passed to block construction. @@ -2819,7 +2912,7 @@
Parameters:
Source:
@@ -2844,6 +2937,16 @@
Parameters:
+
Returns:
+ + +
+ text for block. +
+ + + + @@ -2855,7 +2958,7 @@
Parameters:
-

generateCode(code)

+

registerPrefix(fill) → {string}

@@ -2863,7 +2966,7 @@

generateC
- Assemble the code produced by blocks into a single string. + Get the prefix for registering blocks.
@@ -2899,19 +3002,13 @@

Parameters:
- code + fill string -| - -Array.<string> -| - -number @@ -2921,7 +3018,7 @@
Parameters:
- input + Comma-separated list of quoted strings identifying pipelines to wait for. @@ -2962,7 +3059,7 @@
Parameters:
Source:
@@ -2991,11 +3088,23 @@
Returns:
- a single string + Text to insert into generated code.
+
+
+ Type +
+
+ +string + + +
+
+ @@ -3008,7 +3117,7 @@
Returns:
-

generateCodePane()

+

registerSuffix(fill) → {string}

@@ -3016,7 +3125,7 @@

gener
- Toggle between block input and text input panes. + Get the suffix for registering blocks.
@@ -3027,6 +3136,55 @@

gener +

Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
fill + + +string + + + + Single quoted string identifying pipeline produced.
+ + @@ -3060,7 +3218,7 @@

gener
Source:
@@ -3085,6 +3243,28 @@

gener +

Returns:
+ + +
+ Text to insert into generated code. +
+ + + +
+
+ Type +
+
+ +string + + +
+
+ + @@ -3096,7 +3276,7 @@

gener -

initializeDisplay()

+

runCode()

@@ -3104,8 +3284,8 @@

init
- Set the display property of the two input toggleable panes. -(Has to be done manually rather than in CSS because properties are being reset.) + Run the code generated from the user's blocks. +Depends on the global TidyBlocksWorkspace variable.
@@ -3149,7 +3329,7 @@

init
Source:
@@ -3185,7 +3365,7 @@

init -

json2table(json)

+

saveCode()

@@ -3193,8 +3373,8 @@

json2table<
- Create dynamic table from array from JSON with one table column per property. -Each object must have the same properties. + Save the code generated from the user's workspace. +Depends on the global TidyBlocksWorkspace variable.
@@ -3205,55 +3385,6 @@

json2table< -

Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
json - - -JSON - - - - JSON object to convert to table.
- - @@ -3287,7 +3418,7 @@
Parameters:
Source:
@@ -3323,7 +3454,7 @@
Parameters:
-

loadBlockFiles()

+

setUpBlockly()

@@ -3331,8 +3462,8 @@

loadBlo
- Read 'index.html', find block files, and eval those. -Does _not_ read R files (for now). + Set up Blockly display by injecting XML data into blockDisplay div. +As a side effect, sets the global TidyBlocksWorkspace variable for later use.
@@ -3376,7 +3507,7 @@

loadBlo
Source:
@@ -3412,7 +3543,7 @@

loadBlo -

loadCode(fileList)

+

showCode()

@@ -3420,8 +3551,7 @@

loadCode - Load saved code. -Depends on the global TidyBlocksWorkspace variable. + Show the text based code corresponding to selected blocks. @@ -3432,55 +3562,6 @@

loadCodeParameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
fileList - - -Array.<string> - - - - List of files (only first element is valid).
- - @@ -3514,7 +3595,7 @@
Parameters:
Source:
@@ -3550,7 +3631,7 @@
Parameters:
-

makeBlock(blockName, settings)

+

tbAdd(blockId, row, getLeft, getRight)

@@ -3558,9 +3639,7 @@

makeBlock - Make a block by name. If the construction function returns a string, that's -what we want; otherwise, it's a two-element list with the desired text and -the order, so we return the first element. + Add two values. @@ -3596,13 +3675,13 @@
Parameters:
- blockName + blockId -string +number @@ -3612,14 +3691,14 @@
Parameters:
- must match string name of block. + The ID of the block. - settings + row @@ -3635,7 +3714,53 @@
Parameters:
- settings passed to block construction. + The row to get values from. + + + + + + + getLeft + + + + + +function + + + + + + + + + + How to get the left value from the row. + + + + + + + getRight + + + + + +function + + + + + + + + + + How to get the right value from the row. @@ -3676,7 +3801,7 @@
Parameters:
Source:
@@ -3705,7 +3830,7 @@
Returns:
- text for block. + The sum.
@@ -3722,7 +3847,7 @@
Returns:
-

readCSV(url)

+

tbAnd(blockId, row, getLeft, getRight)

@@ -3730,7 +3855,7 @@

readCSV - Read CSV from a URL and parse to create TidyBlocks data frame. + Logical conjunction of two values. @@ -3766,13 +3891,82 @@
Parameters:
- url + blockId -string +number + + + + + + + + + + The ID of the block. + + + + + + + row + + + + + +Object + + + + + + + + + + The row to get values from. + + + + + + + getLeft + + + + + +function + + + + + + + + + + How to get the left value from the row. + + + + + + + getRight + + + + + +function @@ -3782,7 +3976,7 @@
Parameters:
- URL to read from. + How to get the right value from the row. @@ -3823,7 +4017,7 @@
Parameters:
Source:
@@ -3848,6 +4042,16 @@
Parameters:
+
Returns:
+ + +
+ The conjunction. +
+ + + + @@ -3859,7 +4063,7 @@
Parameters:
-

readCSV(url)

+

tbAssert(check, message)

@@ -3867,8 +4071,7 @@

readCSV - Read a CSV file. Defined here to (a) load local CSV and (b) be in scope for -'eval' of generated code. + Raise exception if a condition doesn't hold. @@ -3904,7 +4107,30 @@
Parameters:
- url + check + + + + + +Boolean + + + + + + + + + + Condition that must be true. + + + + + + + message @@ -3920,7 +4146,7 @@
Parameters:
- URL of data. + What to say if it isn't. @@ -3961,7 +4187,7 @@
Parameters:
Source:
@@ -3986,16 +4212,6 @@
Parameters:
-
Returns:
- - -
- dataframe containing that data. -
- - - - @@ -4007,7 +4223,7 @@
Returns:
-

registerPrefix(fill) → {string}

+

tbAssertNumber(value)

@@ -4015,7 +4231,7 @@

registe
- Get the prefix for registering blocks. + Check that a value is numeric.
@@ -4051,23 +4267,18 @@

Parameters:
- fill + value - -string - - - - Comma-separated list of quoted strings identifying pipelines to wait for. + What to check. @@ -4108,7 +4319,7 @@
Parameters:
Source:
@@ -4137,23 +4348,11 @@
Returns:
- Text to insert into generated code. + The input value if it passes the test.
-
-
- Type -
-
- -string - - -
-
- @@ -4166,7 +4365,7 @@
Returns:
-

registerSuffix(fill) → {string}

+

tbCount(values) → {number}

@@ -4174,7 +4373,7 @@

registe
- Get the suffix for registering blocks. + Count number of values.
@@ -4210,13 +4409,13 @@

Parameters:
- fill + values -string +Array @@ -4226,7 +4425,7 @@
Parameters:
- Single quoted string identifying pipeline produced. + The values to be counted. @@ -4267,7 +4466,7 @@
Parameters:
Source:
@@ -4296,7 +4495,7 @@
Returns:
- Text to insert into generated code. + Number of values.
@@ -4307,7 +4506,7 @@
Returns:
-string +number
@@ -4325,7 +4524,7 @@
Returns:
-

resetDisplay()

+

tbDiv(blockId, row, getLeft, getRight)

@@ -4333,7 +4532,7 @@

resetDisp
- Reset the displays (clear old data between tests). + Divide two values.
@@ -4344,93 +4543,122 @@

resetDisp - - - - -
- - - +
Parameters:
- + + + + + + - + - + - + - + + + + - + + + + - + - -
Source:
-
- + - + - + + - - - - - - - - + + + + + + + + + + + + + + + - - + - -

runCode()

- + + + + + + - -
- Run the code generated from the user's blocks. -Depends on the global TidyBlocksWorkspace variable. -
- + + + + + + + +
NameTypeDescription
blockId + + +number - - + + The ID of the block.
row + + +Object + + The row to get values from.
getLeft + + +function + + How to get the left value from the row.
getRight + + +function + + How to get the right value from the row.
@@ -4466,7 +4694,7 @@

runCodeSource:
@@ -4491,6 +4719,16 @@

runCodeReturns:

+ + +
+ The quotient. +
+ + + + @@ -4502,7 +4740,7 @@

runCodesaveCode()

+

tbEq(blockId, row, getLeft, getRight)

@@ -4510,8 +4748,7 @@

saveCode - Save the code generated from the user's workspace. -Depends on the global TidyBlocksWorkspace variable. + Equality. @@ -4522,85 +4759,644 @@

saveCodeParameters:

+ + + + + + + + + -
+ - +
+ + + - + + + + - + - + - + - + + - + + + + - + - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
blockId + + +number - - + + The ID of the block.
row + + +Object - -
Source:
-
- - + +
The row to get values from.
getLeft + + +function + + How to get the left value from the row.
getRight + + +function + + How to get the right value from the row.
- - +
-

setUpBlockly()

+ + + -
- Set up Blockly display by injecting XML data into blockDisplay div. -As a side effect, sets the global TidyBlocksWorkspace variable for later use. + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ The comparison's result. +
+ + + + + + + + + + + + + + + +

tbExp(blockId, row, getLeft, getRight)

+ + + + + + +
+ Calculate an exponent. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
blockId + + +number + + + + The ID of the block.
row + + +Object + + + + The row to get values from.
getLeft + + +function + + + + How to get the left value from the row.
getRight + + +function + + + + How to get the right value from the row.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ The exponentiated value. +
+ + + + + + + + + + + + + + + +

tbGeq(blockId, row, getLeft, getRight)

+ + + + + + +
+ Greater than or equal. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
blockId + + +number + + + + The ID of the block.
row + + +Object + + + + The row to get values from.
getLeft + + +function + + + + How to get the left value from the row.
getRight + + +function + + + + How to get the right value from the row.
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ The comparison's result. +
+ + + + + + + + + + + + + + + +

tbGet(row, column)

+ + + + + + +
+ Get a column's value from a row, failing if the column doesn't exist.
@@ -4611,6 +5407,78 @@

setUpBloc +

Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
row + + +Object + + + + The row to look in.
column + + +string + + + + The field to look up.
+ + @@ -4644,59 +5512,187 @@

setUpBloc
Source:
- + + + + + +

+ + + + + + + + + + + + + + + +

Returns:
+ + +
+ The value. +
+ + + + + + + + + + + + + + + +

tbGt(blockId, row, getLeft, getRight)

+ + + + + + +
+ Strict greater than. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - -

showCode()

- + + + + + + - -
- Show the text based code corresponding to selected blocks. -
- + + + + + + + +
NameTypeDescription
blockId + + +number + + The ID of the block.
row + + +Object + + The row to get values from.
getLeft + + +function + + How to get the left value from the row.
getRight + + +function + + How to get the right value from the row.
@@ -4732,7 +5728,7 @@

showCodeSource:
@@ -4757,6 +5753,16 @@

showCodeReturns:

+ + +
+ The comparison's result. +
+ + + + @@ -4768,7 +5774,7 @@

showCodetbAdd(row, getLeft, getRight)

+

tbIfElse(blockId, row, getCond, getLeft, getRight)

@@ -4776,7 +5782,7 @@

tbAdd - Add two values. + Choosing a value based on a logical condition. @@ -4810,6 +5816,29 @@
Parameters:
+ + + blockId + + + + + +number + + + + + + + + + + The ID of the block. + + + + row @@ -4833,6 +5862,29 @@
Parameters:
+ + + getCond + + + + + +function + + + + + + + + + + How to get the condition's value. + + + + getLeft @@ -4915,7 +5967,7 @@
Parameters:
Source:
@@ -4944,7 +5996,7 @@
Returns:
- The sum. + The left (right) value if the condition is true (false).
@@ -4961,7 +6013,7 @@
Returns:
-

tbAnd(row, getLeft, getRight)

+

tbIsBoolean(row, getValue)

@@ -4969,7 +6021,7 @@

tbAnd - Logical conjunction of two values. + Check if value is Boolean. @@ -5021,37 +6073,14 @@
Parameters:
- The row to get values from. - - - - - - - getLeft - - - - - -function - - - - - - - - - - How to get the left value from the row. + Row containing values. - getRight + getValue @@ -5067,7 +6096,7 @@
Parameters:
- How to get the right value from the row. + How to get desired value. @@ -5108,7 +6137,7 @@
Parameters:
Source:
@@ -5137,7 +6166,7 @@
Returns:
- The conjunction. + Is value Boolean?
@@ -5154,7 +6183,7 @@
Returns:
-

tbAssert(check, message)

+

tbIsNumber(row, getValue)

@@ -5162,7 +6191,7 @@

tbAssert - Raise exception if a condition doesn't hold. + Check if value is number. @@ -5198,13 +6227,13 @@
Parameters:
- check + row -Boolean +Object @@ -5214,20 +6243,20 @@
Parameters:
- Condition that must be true. + Row containing values. - message + getValue -string +function @@ -5237,7 +6266,7 @@
Parameters:
- What to say if it isn't. + How to get desired value. @@ -5278,7 +6307,7 @@
Parameters:
Source:
@@ -5303,6 +6332,16 @@
Parameters:
+
Returns:
+ + +
+ Is value numeric? +
+ + + + @@ -5314,7 +6353,7 @@
Parameters:
-

tbCount(values) → {number}

+

tbIsString(row, getValue)

@@ -5322,7 +6361,7 @@

tbCount - Count number of values. + Check if value is string. @@ -5358,13 +6397,13 @@
Parameters:
- values + row -Array +Object @@ -5374,7 +6413,30 @@
Parameters:
- The values to be counted. + Row containing values. + + + + + + + getValue + + + + + +function + + + + + + + + + + How to get desired value. @@ -5415,7 +6477,7 @@
Parameters:
Source:
@@ -5444,23 +6506,11 @@
Returns:
- Number of values. + Is value string?
-
-
- Type -
-
- -number - - -
-
- @@ -5473,7 +6523,7 @@
Returns:
-

tbDiv(row, getLeft, getRight)

+

tbLeq(blockId, row, getLeft, getRight)

@@ -5481,7 +6531,7 @@

tbDiv - Divide two values. + Less than or equal. @@ -5515,6 +6565,29 @@
Parameters:
+ + + blockId + + + + + +number + + + + + + + + + + The ID of the block. + + + + row @@ -5620,7 +6693,7 @@
Parameters:
Source:
@@ -5649,7 +6722,7 @@
Returns:
- The quotient. + The comparison's result.
@@ -5666,7 +6739,7 @@
Returns:
-

tbEq(row, getLeft, getRight)

+

tbLt(blockId, row, getLeft, getRight)

@@ -5674,7 +6747,7 @@

tbEq - Equality. + Strictly less than. @@ -5708,6 +6781,29 @@
Parameters:
+ + + blockId + + + + + +number + + + + + + + + + + The ID of the block. + + + + row @@ -5813,7 +6909,7 @@
Parameters:
Source:
@@ -5859,7 +6955,7 @@
Returns:
-

tbExp(row, getLeft, getRight)

+

tbMax(values) → {number}

@@ -5867,7 +6963,7 @@

tbExp - Calculate an exponent. + Find maximum value. @@ -5903,59 +6999,13 @@
Parameters:
- row - - - - - -Object - - - - - - - - - - The row to get values from. - - - - - - - getLeft - - - - - -function - - - - - - - - - - How to get the left value from the row. - - - - - - - getRight + values -function +Array @@ -5965,7 +7015,7 @@
Parameters:
- How to get the right value from the row. + The values to be searched. @@ -6006,7 +7056,7 @@
Parameters:
Source:
@@ -6035,11 +7085,23 @@
Returns:
- The exponentiated value. + Maximum value.
+
+
+ Type +
+
+ +number + + +
+
+ @@ -6052,7 +7114,7 @@
Returns:
-

tbGeq(row, getLeft, getRight)

+

tbMean(values) → {number}

@@ -6060,7 +7122,7 @@

tbGeq - Greater than or equal. + Find mean value. @@ -6096,59 +7158,13 @@
Parameters:
- row - - - - - -Object - - - - - - - - - - The row to get values from. - - - - - - - getLeft - - - - - -function - - - - - - - - - - How to get the left value from the row. - - - - - - - getRight + values -function +Array @@ -6158,7 +7174,7 @@
Parameters:
- How to get the right value from the row. + The values to be averaged. @@ -6199,7 +7215,7 @@
Parameters:
Source:
@@ -6228,11 +7244,23 @@
Returns:
- The comparison's result. + Mean value.
+
+
+ Type +
+
+ +number + + +
+
+ @@ -6245,7 +7273,7 @@
Returns:
-

tbGet(row, key)

+

tbMedian(values) → {number}

@@ -6253,7 +7281,7 @@

tbGet - Get a column's value from a row, failing if the column doesn't exist. + Find median value. @@ -6289,36 +7317,13 @@
Parameters:
- row - - - - - -Object - - - - - - - - - - The row to look in. - - - - - - - key + values -string +Array @@ -6328,7 +7333,7 @@
Parameters:
- The field to look up. + The values to be searched. @@ -6369,7 +7374,7 @@
Parameters:
Source:
@@ -6398,11 +7403,23 @@
Returns:
- The value. + Median value.
+
+
+ Type +
+
+ +number + + +
+
+ @@ -6415,7 +7432,7 @@
Returns:
-

tbGt(row, getLeft, getRight)

+

tbMin(values) → {number}

@@ -6423,7 +7440,7 @@

tbGt - Strict greater than. + Find median value. @@ -6434,84 +7451,38 @@

tbGt - - - - Name - - - Type - - - - - - Description - - - - - - - - - row - - - - - -Object - - - - - - - - - - The row to get values from. - - - - - - - getLeft - - - - - -function +
Parameters:
+ + + + + + + - - + - + - + - - + + + + - + + @@ -6562,7 +7533,7 @@
Parameters:
Source:
@@ -6591,11 +7562,23 @@
Returns:
- The comparison's result. + Minimum value.
+
+
+ Type +
+
+ +number + + +
+
+ @@ -6608,7 +7591,7 @@
Returns:
-

tbIsNumber(value)

+

tbMod(blockId, row, getLeft, getRight)

@@ -6616,7 +7599,7 @@

tbIsNumber<
- Check that a value is numeric. + Find the remainder of two values.
@@ -6652,18 +7635,92 @@

Parameters:
- + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6704,7 +7761,7 @@
Parameters:
Source:
@@ -6733,7 +7790,7 @@
Returns:
- The input value if it passes the test. + The remainder.
@@ -6750,7 +7807,7 @@
Returns:
-

tbLeq(row, getLeft, getRight)

+

tbMul(blockId, row, getLeft, getRight)

@@ -6758,7 +7815,7 @@

tbLeq - Less than or equal. + Multiply two values. @@ -6792,6 +7849,29 @@
Parameters:

+ + + + + + + + + + + + + + + + @@ -6897,7 +7977,7 @@
Parameters:
Source:
@@ -6926,7 +8006,7 @@
Returns:
- The comparison's result. + The product.
@@ -6943,7 +8023,7 @@
Returns:
-

tbLt(row, getLeft, getRight)

+

tbNeg(blockId, row, getValue)

@@ -6951,7 +8031,7 @@

tbLt - Strictly less than. + Negate a value. @@ -6987,13 +8067,13 @@
Parameters:

- + + - + + - + + @@ -7090,7 +8170,7 @@
Parameters:
Source:
@@ -7119,7 +8199,7 @@
Returns:
- The comparison's result. + The numerical negation.
@@ -7136,7 +8216,7 @@
Returns:
-

tbMax(values) → {number}

+

tbNeq(blockId, row, getLeft, getRight)

@@ -7144,7 +8224,7 @@

tbMax - Find maximum value. + Inequality. @@ -7180,13 +8260,13 @@
Parameters:

- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7237,7 +8386,7 @@
Parameters:
Source:
@@ -7262,26 +8411,14 @@
Parameters:
-
Returns:
- - -
- Maximum value. -
- - +
Returns:
-
-
- Type -
-
-number +
+ The comparison's result. +
-
-
@@ -7295,7 +8432,7 @@
Returns:
-

tbMean(values) → {number}

+

tbNot(blockId, row, getValue)

@@ -7303,7 +8440,7 @@

tbMean - Find mean value. + Logical negation of a value. @@ -7339,13 +8476,13 @@
Parameters:

- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7396,7 +8579,7 @@
Parameters:
Source:
@@ -7425,23 +8608,11 @@
Returns:
- Mean value. + The logical conjunction.
-
-
- Type -
-
- -number - - -
-
- @@ -7454,7 +8625,7 @@
Returns:
-

tbMedian(values) → {number}

+

tbOr(blockId, row, getLeft, getRight)

@@ -7462,7 +8633,7 @@

tbMedian - Find median value. + Logical disjunction of two values. @@ -7498,13 +8669,13 @@
Parameters:

- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7555,7 +8795,7 @@
Parameters:
Source:
@@ -7584,23 +8824,11 @@
Returns:
- Median value. + The disjunction.
-
-
- Type -
-
- -number - - -
-
- @@ -7613,7 +8841,7 @@
Returns:
-

tbMin(values) → {number}

+

tbStd(values) → {number}

@@ -7621,7 +8849,7 @@

tbMin - Find median value. + Find standard deviation. @@ -7673,7 +8901,7 @@
Parameters:
-

+ @@ -7714,7 +8942,7 @@
Parameters:
Source:
@@ -7743,7 +8971,7 @@
Returns:
- Minimum value. + Standard deviation.
@@ -7772,7 +9000,7 @@
Returns:
-

tbMod(row, getLeft, getRight)

+

tbSub(blockId, row, getLeft, getRight)

@@ -7780,7 +9008,7 @@

tbMod - Find the remainder of two values. + Subtract two values. @@ -7814,6 +9042,29 @@
Parameters:

+ + + + + + + + + + + + + + + + @@ -7919,7 +9170,7 @@
Parameters:
Source:
@@ -7948,7 +9199,7 @@
Returns:
- The remainder. + The difference.
@@ -7965,7 +9216,7 @@
Returns:
-

tbMul(row, getLeft, getRight)

+

tbSum(values) → {number}

@@ -7973,7 +9224,7 @@

tbMul - Multiply two values. + Find sum. @@ -8009,59 +9260,13 @@
Parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -8112,7 +9317,7 @@
Parameters:
Source:
@@ -8141,11 +9346,23 @@
Returns:
- The product. + Total.
+
+
+ Type +
+
+ +number + + +
+
+ @@ -8158,7 +9375,7 @@
Returns:
-

tbNeg(row, getValue)

+

tbToBoolean({number{, row, getValue)

@@ -8166,7 +9383,7 @@

tbNeg - Negate a value. + Convert row value to Boolean. @@ -8200,6 +9417,24 @@
Parameters:

+ + + + + + + + + + + + + + + + @@ -8218,7 +9453,7 @@
Parameters:
- + @@ -8241,7 +9476,7 @@
Parameters:
- + @@ -8282,7 +9517,7 @@
Parameters:
Source:
@@ -8311,7 +9546,7 @@
Returns:
- The numerical negation. + Boolean value.
@@ -8328,7 +9563,7 @@
Returns:
-

tbNeq(row, getLeft, getRight)

+

tbToDatetime({number{, row, getValue)

@@ -8336,7 +9571,7 @@

tbNeq - Inequality. + Convert row value to datetime. @@ -8372,36 +9607,31 @@
Parameters:

- + - + - + + - + + @@ -8475,7 +9705,7 @@
Parameters:
Source:
@@ -8504,7 +9734,7 @@
Returns:
- The comparison's result. + Date object.
@@ -8521,7 +9751,7 @@
Returns:
-

tbNot(row, getValue)

+

tbToDay(row, getValue)

@@ -8529,7 +9759,7 @@

tbNot - Logical negation of a value. + Extract day of month from value. @@ -8581,7 +9811,7 @@
Parameters:
-

+ @@ -8604,7 +9834,7 @@
Parameters:
- + @@ -8645,7 +9875,7 @@
Parameters:
Source:
@@ -8674,7 +9904,7 @@
Returns:
- The logical conjunction. + Day of month as number.
@@ -8691,7 +9921,7 @@
Returns:
-

tbOr(row, getLeft, getRight)

+

tbToHours(row, getValue)

@@ -8699,7 +9929,7 @@

tbOr - Logical disjunction of two values. + Extract hours from date value. @@ -8751,37 +9981,14 @@
Parameters:
-

- - - - - - - - - - - - - - - - + - + + @@ -8838,7 +10045,7 @@
Parameters:
Source:
@@ -8867,7 +10074,7 @@
Returns:
- The disjunction. + Hours portion of value.
@@ -8884,7 +10091,7 @@
Returns:
-

tbStd(values) → {number}

+

tbToMinutes(row, getValue)

@@ -8892,7 +10099,7 @@

tbStd - Find standard deviation. + Extract minutes from date value. @@ -8928,13 +10135,13 @@
Parameters:

- + + + + + + + + + + + + + + + + + + @@ -8985,7 +10215,7 @@
Parameters:
Source:
@@ -9014,23 +10244,11 @@
Returns:
- Standard deviation. + Minutes portion of value.
-
-
- Type -
-
- -number - - -
-
- @@ -9043,7 +10261,7 @@
Returns:
-

tbSub(row, getLeft, getRight)

+

tbToMonth(row, getValue)

@@ -9051,7 +10269,7 @@

tbSub - Subtract two values. + Extract month from value. @@ -9103,37 +10321,14 @@
Parameters:
-

- - - - - - - - - - - - - - - - + - + + @@ -9190,7 +10385,7 @@
Parameters:
Source:
@@ -9219,7 +10414,7 @@
Returns:
- The difference. + Month as number.
@@ -9236,7 +10431,7 @@
Returns:
-

tbSum(values) → {number}

+

tbToNumber({number{, row, getValue)

@@ -9244,7 +10439,7 @@

tbSum - Find sum. + Convert row value to number. @@ -9280,13 +10475,31 @@
Parameters:

- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -9337,7 +10573,7 @@
Parameters:
Source:
@@ -9366,23 +10602,11 @@
Returns:
- Total. + Numeric value.
-
-
- Type -
-
- -number - - -
-
- @@ -9395,7 +10619,7 @@
Returns:
-

tbToBoolean(row, getValue)

+

tbToSeconds(row, getValue)

@@ -9403,7 +10627,7 @@

tbToBoolea
- Convert row value to Boolean. + Extract seconds from date value.
@@ -9519,7 +10743,7 @@

Parameters:
Source:
@@ -9548,7 +10772,7 @@
Returns:
- Boolean value. + Seconds portion of value.
@@ -9565,7 +10789,7 @@
Returns:
-

tbToNumber(row, getValue)

+

tbToString({number{, row, getValue)

@@ -9573,7 +10797,7 @@

tbToNumber<
- Convert row value to number. + Convert row value to string.
@@ -9607,6 +10831,24 @@

Parameters:
+ + + + + + + + + + + + + + + + @@ -9689,7 +10931,7 @@
Parameters:
Source:
@@ -9718,7 +10960,7 @@
Returns:
- Numeric value. + String value.
@@ -9735,7 +10977,7 @@
Returns:
-

tbToString(row, getValue)

+

tbToWeekDay(row, getValue)

@@ -9743,7 +10985,7 @@

tbToString<
- Convert row value to string. + Extract day of week from value.
@@ -9859,7 +11101,7 @@

Parameters:
Source:
@@ -9888,7 +11130,7 @@
Returns:
- String value. + Day of month as number.
@@ -10019,7 +11261,7 @@
Parameters:
Source:
@@ -10156,7 +11398,7 @@
Parameters:
Source:
@@ -10224,13 +11466,13 @@
Returns:

- Documentation generated by JSDoc 3.6.3 on Thu Sep 05 2019 20:42:06 GMT-0400 (Eastern Daylight Time) + Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:03 GMT-0400 (Eastern Daylight Time)
diff --git a/docs/gui.js.html b/docs/gui.js.html new file mode 100644 index 000000000..d6ca5831d --- /dev/null +++ b/docs/gui.js.html @@ -0,0 +1,317 @@ + + + + + JSDoc: Source: gui.js + + + + + + + + + + +
+ +

Source: gui.js

+ + + + + + +
+
+
// Share the workspace between functions.
+let TidyBlocksWorkspace = null
+
+// Regular expressions to match valid single column names and multiple column names.
+const SINGLE_COLUMN_NAME = /^ *[_A-Za-z][_A-Za-z0-9]* *$/
+const MULTIPLE_COLUMN_NAMES = /^ *([_A-Za-z][_A-Za-z0-9]*)( *, *[_A-Za-z][_A-Za-z0-9]*)* *$/
+
+// Names of single-column fields in various blocks (for generating validators).
+const SINGLE_COLUMN_FIELDS = [
+  'COLUMN',
+  'FORMAT',
+  'LEFT_TABLE',
+  'LEFT_COLUMN',
+  'RIGHT_TABLE',
+  'RIGHT_COLUMN',
+  'NAME',
+  'COLOR',
+  'X_AXIS',
+  'Y_AXIS'
+]
+
+// Names of multiple-column fields in various blocks (for generating validators).
+const MULTIPLE_COLUMN_FIELDS = [
+  'MULTIPLE_COLUMNS'
+]
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Class to handle connections to the outside world.  (A different class with
+ * the same methods is used for testing purposes.)
+ */
+class GuiEnvironment {
+
+  constructor () {
+  }
+
+  /**
+   * Get the code to run.
+   * @returns {string} The code to run.
+   */
+  getCode () {
+    return Blockly.JavaScript.workspaceToCode(TidyBlocksWorkspace)
+  }
+
+  /**
+   * Read CSV from a URL and parse to create TidyBlocks data frame.
+   * @param {string} url URL to read from.
+   */
+  readCSV (url) {
+    const request = new XMLHttpRequest()
+    request.open('GET', url, false)
+    request.send(null)
+
+    if (request.status !== 200) {
+      console.log(`ERROR: ${request.status}`)
+      return null
+    }
+    else {
+      return csv2TidyBlocksDataFrame(request.responseText, Papa.parse)
+    }
+  }
+
+  /**
+   * Display a plot.
+   * @param {Object} spec Vega-Lite spec for plot with data filled in.
+   */
+  displayPlot (spec) {
+    vegaEmbed('#plotOutput', spec, {})
+  }
+
+  /**
+   * Display a table (as HTML).
+   * @param {Object} table JSON array of uniform objects.
+   */
+  displayTable (table) {
+    document.getElementById('dataOutput').innerHTML = json2table(table)
+  }
+
+  /**
+   * Display an error.
+   * @param {string} error The message to display.
+   */
+  displayError (error) {
+    document.getElementById('error').innerHTML = `<p>${error}</p>`
+  }
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Set the display property of the two input toggleable panes.
+ * (Has to be done manually rather than in CSS because properties are being reset.)
+ */
+const initializeDisplay = () => {
+  for (let [nodeId, state] of [
+    ['codeOutput', 'none'],
+    ['blockDisplay', 'block']]) {
+    document.getElementById(nodeId).style.display = state
+  }
+  document.getElementById('dataButton').click()
+}
+
+/**
+ * Toggle between block input and text input panes.
+ */
+const generateCodePane = () => {
+  for (let nodeId of ['codeOutput', 'blockDisplay']) {
+    const node = document.getElementById(nodeId)
+    if (node.style.display === 'none') {
+      node.style.display = 'block'
+    }
+    else {
+      node.style.display = 'none'
+    }
+  }
+}
+
+/**
+ * Show the text based code corresponding to selected blocks.
+ */
+const showCode = () => {
+  const code = Blockly.JavaScript.workspaceToCode(TidyBlocksWorkspace)
+  document.getElementById('codeOutput').innerHTML = code
+}
+
+/**
+ * Set up Blockly display by injecting XML data into blockDisplay div.
+ * As a side effect, sets the global TidyBlocksWorkspace variable for later use.
+ */
+const setUpBlockly = () => {
+  TidyBlocksWorkspace = Blockly.inject(
+    document.getElementById('blockDisplay'),
+    {
+      media: 'media/',
+      toolbox: document.getElementById('toolbox'),
+      horizontalLayout: false,
+      scrollbars: false, 
+      theme: Blockly.Themes.Tidy
+    }
+  )
+
+  //TidyBlocksWorkspace.addChangeListener(Blockly.Events.disableOrphans)
+
+  TidyBlocksWorkspace.addChangeListener((event) => {
+    if (event.type === Blockly.Events.CREATE) {
+      const block = TidyBlocksWorkspace.getBlockById(event.blockId)
+      TidyBlocksManager.addNewBlock(block)
+    }
+    else if (event.type === Blockly.Events.DELETE) {
+      // FIXME: handle deletion
+    }
+  })
+
+  SINGLE_COLUMN_FIELDS.forEach(col => {
+    Blockly.Extensions.register(`validate_${col}`, createValidator(col, SINGLE_COLUMN_NAME))
+  })
+
+  MULTIPLE_COLUMN_FIELDS.forEach(col => {
+    Blockly.Extensions.register(`validate_${col}`, createValidator(col, MULTIPLE_COLUMN_NAMES))
+  })
+}
+
+/**
+ * Create a Blockly field validation function for a column.
+ * See https://developers.google.com/blockly/guides/create-custom-blocks/fields/validators
+ * and https://developers.google.com/blockly/guides/create-custom-blocks/extensions for details.
+ * @param {string} columnName Name of column to be validated.
+ * @param {regex} pattern Regular expression that must be matched.
+ * @returns A function (defined with old-style syntax so that 'this' manipulation will work) to validate column values.
+ */
+const createValidator = (columnName, pattern) => {
+  return function () {
+    const field = this.getField(columnName)
+    field.setValidator((newValue) => {
+      if (newValue.match(pattern)) {
+        return newValue.trim() // strip leading and trailing spaces
+      }
+      return null // fails validation
+    })
+  }
+}
+
+/**
+ * Run the code generated from the user's blocks.
+ * Depends on the global TidyBlocksWorkspace variable.
+ */
+const runCode = () => {
+  Blockly.JavaScript.INFINITE_LOOP_TRAP = null
+  TidyBlocksManager.run(new GuiEnvironment())
+}
+
+/**
+ * Save the code generated from the user's workspace.
+ * Depends on the global TidyBlocksWorkspace variable.
+ */
+const saveCode = () => {
+  const filename = document.getElementById('filename').value
+  if (! filename) {
+    window.alert("Empty filename")
+  }
+  else {
+    const xml = Blockly.Xml.workspaceToDom(TidyBlocksWorkspace)
+    const text = Blockly.Xml.domToText(xml)
+    const link = document.getElementById('download')
+    link.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text))
+    link.setAttribute('download', filename)
+  }
+}
+
+/**
+ * Load saved code.
+ * Depends on the global TidyBlocksWorkspace variable.
+ * @param {string[]} fileList List of files (only first element is valid).
+ */
+const loadCode = (fileList) => {
+  const file = fileList[0]
+  const text = file.text().then((text) => {
+    const xml = Blockly.Xml.textToDom(text)
+    Blockly.Xml.clearWorkspaceAndLoadFromXml(xml, TidyBlocksWorkspace)
+  })
+}
+
+/**
+ * Produce a human-friendly name for the type of a column.
+ * @param value The value whose type is checked.
+ * @returns The name of the type
+ */
+const colTypeName = (value) => {
+  if (value instanceof Date) {
+    return 'datetime'
+  }
+  return typeof value
+}
+
+/**
+ * Create dynamic table from array from JSON with one table column per property.
+ * Each object must have the same properties.
+ * @param {JSON} json JSON object to convert to table.
+ */
+const json2table = (json) => {
+  if (json.length === 0) {
+    return '<p>empty</p>'
+  }
+  const cols = Object.keys(json[0])
+  const headerRow = '<tr>' + cols.map(c => `<th>${c}</th>`).join('') + '</tr>'
+  const typeRow = '<tr>' + cols.map(c => `<th>${colTypeName(json[0][c])}</th>`).join('') + '</tr>'
+  const bodyRows = json.map(row => {
+    return '<tr>' + cols.map(c => `<td>${row[c]}</td>`).join('') + '</tr>'
+  }).join('')
+  return `<table><thead>${headerRow}</thead><tbody>${typeRow}${bodyRows}</tbody></table>`
+}
+
+/**
+ * Toggle between tabs for dataframe and error pane
+ */
+const displayTab = (event, tabName) => {
+  Array.from(document.getElementsByClassName('tabContent')).forEach(element => {
+    element.style.display = 'none'
+  })
+  Array.from(document.getElementsByClassName('tablink')).forEach(element => {
+    element.className = element.classList.remove('active')
+  })
+  document.getElementById(tabName).style.display = 'block';
+  event.currentTarget.classList.add('active')
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:23:53 GMT-0400 (Eastern Daylight Time) +
+ + + + + diff --git a/docs/index.html b/docs/index.html index 67959745a..d4a6353cc 100644 --- a/docs/index.html +++ b/docs/index.html @@ -68,13 +68,13 @@

Contributors


- Documentation generated by JSDoc 3.6.3 on Thu Sep 05 2019 20:42:06 GMT-0400 (Eastern Daylight Time) + Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:03 GMT-0400 (Eastern Daylight Time)
diff --git a/docs/test_utils.js.html b/docs/test_utils.js.html new file mode 100644 index 000000000..eb01bd187 --- /dev/null +++ b/docs/test_utils.js.html @@ -0,0 +1,371 @@ + + + + + JSDoc: Source: test/utils.js + + + + + + + + + + +
+ +

Source: test/utils.js

+ + + + + + +
+
+
const assert = require('assert')
+const fs = require('fs')
+const {parse} = require('node-html-parser')
+const Papa = require('papaparse')
+
+//
+// Loading our own utilities using 'require' instead of relying on them to be
+// loaded by the browser takes a bit of hacking. We put the current directory on
+// the module search path, then 'require' the files. Inside those files, we
+// check if 'module' is defined before trying to define the exports.
+//
+module.paths.unshift(process.cwd())
+const {
+  csv2TidyBlocksDataFrame,
+  registerPrefix,
+  registerSuffix,
+  TidyBlocksDataFrame,
+  TidyBlocksManager
+} = require('tidyblocks/tidyblocks')
+
+/**
+ * Assert that an object has a key.
+ * @param {string} actual Object being examined.
+ * @param {string} required Key that must be present.
+ * @param {string} message Error message.
+ */
+const assert_hasKey = (actual, required, message) => {
+  if (! (required in actual)) {
+    throw new assert.AssertionError({
+      message: message,
+      actual: Object.keys(actual),
+      expected: required})
+  }
+}
+
+/**
+ * Assert that one string contains another.
+ * @param {string} actual String being examined.
+ * @param {string} required String to look for.
+ * @param {string} message Error message.
+ */
+const assert_includes = (actual, required, message) => {
+  if (! actual.includes(required)) {
+    throw new assert.AssertionError({
+      message: message,
+      actual: actual,
+      expected: required})
+  }
+}
+
+/**
+ * Assert that a string matches a regular expression.
+ * @param {string} actual String being examined.
+ * @param {regexp} required Pattern to look for.
+ * @param {string} message Error message.
+ */
+const assert_match = (actual, required, message) => {
+  if (! actual.match(required)) {
+    throw new assert.AssertionError({
+      message: message,
+      actual: actual,
+      expected: required})
+  }
+}
+
+/**
+ * Assert that one string starts with another.
+ * @param {string} actual String being examined.
+ * @param {string} required String to look for.
+ * @param {string} message Error message.
+ */
+const assert_startsWith = (actual, required, message) => {
+  if (! actual.startsWith(required)) {
+    throw new assert.AssertionError({
+      message: message,
+      actual: actual,
+      expected: required})
+  }
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Replacement for singleton Blockly object. This defines only the methods and
+ * values used by block creation code.
+ */
+class BlocklyClass {
+  constructor () {
+
+    // Manually-created blocks.
+    this.Blocks = {}
+
+    // JavaScript generation utilities.
+    this.JavaScript = {
+      ORDER_ATOMIC: 'order=atomic',
+      ORDER_EQUALITY: 'order=equality',
+      ORDER_NONE: 'order=none',
+      ORDER_RELATIONAL: 'order=relational',
+      ORDER_UNARY_NEGATION: 'order=negation',
+
+      quote_: (value) => {
+        return `"${value}"`
+      },
+
+      valueToCode: (block, field, order) => {
+        return block[field]
+      }
+    }
+
+    // All registered themes.
+    this.Themes = {}
+
+    // Create a new theme.
+    this.Theme = class {
+      constructor (blockStyles, categoryStyles) {
+      }
+    }
+
+    // All fields of known blocks.
+    this.fields = {}
+  }
+
+  // Helper functon to turn JSON into blocks entry.
+  defineBlocksWithJsonArray (allJson) {
+    allJson.forEach(entry => {
+      assert(!(entry.type in this.fields),
+             `Duplicate block of type ${entry.type}`)
+      this.fields[entry.type] = new Set()
+      if ('args0' in entry) {
+        entry.args0.forEach(field => {
+          const name = field.name
+          assert(! this.fields[entry.type].has(name),
+                 `Duplicate field ${name} in ${entry.type}`)
+          this.fields[entry.type].add(name)
+        })
+      }
+    })
+  }
+}
+let Blockly = null;
+
+/**
+ * Placeholder for a block object.
+ */
+class MockBlock {
+  constructor (settings) {
+    Object.assign(this, settings)
+    TidyBlocksManager.addNewBlock(this)
+  }
+
+  getFieldValue (key) {
+    return this[key]
+  }
+}
+
+/**
+ * Make a block by name.  If the construction function returns a string, that's
+ * what we want; otherwise, it's a two-element list with the desired text and
+ * the order, so we return the first element.
+ * @param {string} blockName - must match string name of block.
+ * @param {Object} settings - settings passed to block construction.
+ * @return text for block.
+ */
+const makeBlock = (blockName, settings) => {
+  assert(blockName in Blockly.fields,
+         `Unknown block name "${blockName}"`)
+  Object.keys(settings).forEach(name => {
+    assert(Blockly.fields[blockName].has(name),
+           `Unknown field ${name} in ${blockName}, known fields are ${Array.from(Blockly.fields[blockName]).join(', ')}`)
+  })
+
+  assert(blockName in Blockly.JavaScript,
+         `Unknown block name "${blockName}"`)
+  const result = Blockly.JavaScript[blockName](new MockBlock(settings))
+  if (typeof result === 'string') {
+    return result
+  }
+  else {
+    return result[0]
+  }
+}
+
+/**
+ * Delete an existing block. (Emulates the drag-and-drop delete in the GUI.)
+ */
+const deleteBlock = (block) => {
+  TidyBlocksManager.deleteBlock(block)
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Assemble the code produced by blocks into a single string.
+ * @param code {string|string[]|number} - input
+ * @return a single string
+ */
+const generateCode = (code) => {
+  if (Array.isArray(code)){
+    code = code.join('\n') // multiple blocks
+  }
+  else if (typeof code !== 'string') {
+    code = `${code}` // numbers
+  }
+  return code
+}
+
+/**
+ * Read 'index.html', find block files, and eval those.
+ * Does _not_ read R files (for now).
+ */
+const loadBlockFiles = () => {
+  Blockly = new BlocklyClass()
+  parse(fs.readFileSync('index.html', 'utf-8'))
+    .querySelector('#tidyblocks')
+    .querySelectorAll('script')
+    .map(node => node.attributes.src)
+    .filter(path => !path.includes('/r/'))
+    .map(path => fs.readFileSync(path, 'utf-8'))
+    .forEach(src => eval(src))
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Environment for testing. (Replaces the one in the GUI.)
+ */
+class TestEnvironment {
+  constructor (code) {
+    this.code = code
+    this.table = null
+    this.plot = null
+    this.error = null
+  }
+
+  /**
+   * Get the code to run.
+   * @returns {string} The code to run.
+   */
+  getCode () {
+    return this.code
+  }
+
+  /**
+   * Read a CSV file.  Defined here to (a) load local CSV and (b) be in scope for
+   * 'eval' of generated code.
+   * @param url {string} - URL of data.
+   * @return dataframe containing that data.
+   */
+  readCSV (url) {
+    if (url.includes('raw.githubusercontent.com')) {
+      url = 'data/' + url.split('/').pop()
+    }
+    else if (url.startsWith('test://')) {
+      url = 'test/data/' + url.split('//').pop()
+    }
+    else {
+      assert(false, `Cannot read "${url}" for testing`)
+    }
+    const path = `${process.cwd()}/${url}`
+    const text = fs.readFileSync(path, 'utf-8')
+    return csv2TidyBlocksDataFrame(text, Papa.parse)
+  }
+
+  /**
+   * "Display" a table (record for testing purposes).
+   * @param data {Object} - data to record.
+   */
+  displayTable (data) {
+    this.table = data
+  }
+
+  /**
+   * "Display" a plot (record for testing purposes).
+   * @param spec {Object} - Vega-Lite spec for plot.
+   */
+  displayPlot (spec) {
+    this.plot = spec
+  }
+
+  /**
+   * Display an error (record for testing purposes).
+   * @param error {string} - message to record.
+   */
+  displayError (error) {
+    this.error = error
+  }
+}
+
+/**
+ * Run code block.
+ * @param code {string} - code to evaluate.
+ * @return environment (including eval'd code).
+ */
+const evalCode = (code) => {
+  if (typeof code !== 'string') {
+    code = generateCode(code)
+  }
+  const environment = new TestEnvironment(code)
+  TidyBlocksManager.run(environment)
+  return environment
+}
+
+//
+// Exports.
+//
+module.exports = {
+  csv2TidyBlocksDataFrame,
+  registerPrefix,
+  registerSuffix,
+  TidyBlocksDataFrame,
+  TidyBlocksManager,
+  assert_hasKey,
+  assert_includes,
+  assert_match,
+  assert_startsWith,
+  loadBlockFiles,
+  makeBlock,
+  generateCode,
+  evalCode
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:03 GMT-0400 (Eastern Daylight Time) +
+ + + + + diff --git a/docs/tidyblocks.js.html b/docs/tidyblocks.js.html new file mode 100644 index 000000000..88194dece --- /dev/null +++ b/docs/tidyblocks.js.html @@ -0,0 +1,1229 @@ + + + + + JSDoc: Source: tidyblocks.js + + + + + + + + + + +
+ +

Source: tidyblocks.js

+ + + + + + +
+
+
/**
+ * Prefix and suffix for well-formed pipelines.
+ */
+const TIDYBLOCKS_START = '/* tidyblocks start */'
+const TIDYBLOCKS_END = '/* tidyblocks end */'
+
+/**
+ * Turn block of CSV text into TidyBlocksDataFrame. The parser argument should be Papa.parse;
+ * it is passed in here so that this file can be loaded both in the browser and for testing.
+ * @param {string} text Text to parse.
+ * @param {function} parser Function to turn CSV text into array of objects.
+ * @returns New dataframe with sanitized column headers.
+ */
+const csv2TidyBlocksDataFrame = (text, parser) => {
+
+  const seen = new Map() // global to transformHeader
+  const transformHeader = (name) => {
+    // Simple character fixes.
+    name = name
+      .trim()
+      .replace(/ /g, '_')
+      .replace(/[^A-Za-z0-9_]/g, '')
+
+    // Ensure header is not empty after character fixes.
+    if (name.length === 0) {
+      name = 'EMPTY'
+    }
+
+    // Name must start with underscore or letter.
+    if (! name.match(/^[_A-Za-z]/)) {
+      name = `_${name}`
+    }
+
+    // Name must be unique.
+    if (seen.has(name)) {
+      const serial = seen.get(name) + 1
+      seen.set(name, serial)
+      name = `${name}_${serial}`
+    }
+    else {
+      seen.set(name, 0)
+    }
+
+    return name
+  }
+
+  const result = parser(
+    text.trim(),
+    {
+      dynamicTyping: true,
+      header: true,
+      skipEmptyLines: true,
+      transformHeader: transformHeader
+    }
+  )
+  return new TidyBlocksDataFrame(result.data)
+}
+
+/**
+ * Get the prefix for registering blocks.
+ * @param {string} fill Comma-separated list of quoted strings identifying pipelines to wait for.
+ * @returns {string} Text to insert into generated code.
+ */
+const registerPrefix = (fill) => {
+  return `${TIDYBLOCKS_START} TidyBlocksManager.register([${fill}], () => {`
+}
+
+/**
+ * Get the suffix for registering blocks.
+ * @param {string} fill Single quoted string identifying pipeline produced.
+ * @returns {string} Text to insert into generated code.
+ */
+const registerSuffix = (fill) => {
+  return `}, [${fill}]) ${TIDYBLOCKS_END}`
+}
+
+/**
+ * Fix up runnable code if it isn't properly terminated yet.
+ * @param {string} code Pipeline code to be terminated if necessary.
+ */
+const fixCode = (code) => {
+  if (! code.endsWith(TIDYBLOCKS_END)) {
+    const suffix = registerSuffix('')
+    code += `.plot(environment, {}) ${suffix}`
+  }
+  return code
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Raise exception if a condition doesn't hold.
+ * @param {Boolean} check Condition that must be true.
+ * @param {string} message What to say if it isn't.
+ */
+const tbAssert = (check, message) => {
+  if (! check) {
+    throw new Error(message)
+  }
+}
+
+/**
+ * Check that a value is numeric.
+ * @param value What to check.
+ * @returns The input value if it passes the test.
+ */
+const tbAssertNumber = (value) => {
+  tbAssert(typeof value === 'number',
+           `Value ${value} is not a number`)
+  return value
+}
+
+/**
+ * Check that the types of two values are the same.
+ * @param left One of the values.
+ * @param right The other value.
+ */
+const tbTypeEqual = (left, right) => {
+  tbAssert(typeof left === typeof right,
+           `Values ${left} and ${right} have different types`)
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Count number of values.
+ * @param {Array} values The values to be counted.
+ * @return {number} Number of values.
+ */
+const tbCount = (values) => {
+  return values.length
+}
+
+/**
+ * Find maximum value.
+ * @param {Array} values The values to be searched.
+ * @return {number} Maximum value.
+ */
+const tbMax = (values) => {
+  return (values.length === 0)
+    ? NaN
+    : values.reduce((soFar, val) => (val > soFar) ? val : soFar)
+}
+
+/**
+ * Find mean value.
+ * @param {Array} values The values to be averaged.
+ * @return {number} Mean value.
+ */
+const tbMean = (values) => {
+  return (values.length === 0)
+    ? NaN
+    : values.reduce((total, num) => total + num, 0) / values.length
+}
+
+/**
+ * Find median value.
+ * @param {Array} values The values to be searched.
+ * @return {number} Median value.
+ */
+const tbMedian = (values) => {
+  if (values.length === 0) {
+    return NaN
+  }
+  else {
+    const temp = [...values]
+    temp.sort()
+    return temp[Math.floor(temp.length / 2)]
+  }
+}
+
+/**
+ * Find median value.
+ * @param {Array} values The values to be searched.
+ * @return {number} Minimum value.
+ */
+const tbMin = (values) => {
+  return (values.length === 0)
+    ? NaN
+    : values.reduce((soFar, val) => (val < soFar) ? val : soFar)
+}
+
+/**
+ * Find standard deviation.
+ * @param {Array} values The values to be summarized.
+ * @return {number} Standard deviation.
+ */
+const tbStd = (values) => {
+  return Math.sqrt(tbVariance(values))
+}
+
+/**
+ * Find sum.
+ * @param {Array} values The values to be added.
+ * @return {number} Total.
+ */
+const tbSum = (values) => {
+  return values.reduce((total, num) => total + num, 0)
+}
+
+/**
+ * Find variance.
+ * @param {Array} values The values to be summarized.
+ * @return {number} Variance.
+ */
+const tbVariance = (values) => {
+  if (! values) {
+    return NaN
+  }
+  const m = tbMean(values)
+  const squareDiffs = values.map(v => (v - m)**2)
+  return tbMean(squareDiffs)
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Convert row value to Boolean.
+ * @param {number{ blockId which block this is.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Boolean value.
+ */
+const tbToBoolean = (blockId, row, getValue) => {
+  return getValue(row) ? true : false
+}
+
+/**
+ * Convert row value to datetime.
+ * @param {number{ blockId which block this is.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Date object.
+ */
+const tbToDatetime = (blockId, row, getValue) => {
+  const value = getValue(row)
+  const result = new Date(value)
+  tbAssert(!isNaN(result),
+           `[block ${blockId}] cannot convert "${value}" to date`)
+  return result
+}
+
+/**
+ * Convert row value to number.
+ * @param {number{ blockId which block this is.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Numeric value.
+ */
+const tbToNumber = (blockId, row, getValue) => {
+  const value = getValue(row)
+  if (typeof value == 'boolean') {
+    return value ? 1 : 0
+  }
+  if (typeof value == 'string') {
+    return parseFloat(string)
+  }
+  return value
+}
+
+/**
+ * Convert row value to string.
+ * @param {number{ blockId which block this is.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns String value.
+ */
+const tbToString = (blockId, row, getValue) => {
+  const value = getValue(row)
+  if (typeof value == 'string') {
+    return value
+  }
+  return `${value}`
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Check if value is Boolean.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Is value Boolean?
+ */
+const tbIsBoolean = (row, getValue) => {
+  return typeof getValue(row) === 'boolean'
+}
+
+/**
+ * Check if value is number.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Is value numeric?
+ */
+const tbIsNumber = (row, getValue) => {
+  return typeof getValue(row) === 'number'
+}
+
+/**
+ * Check if value is string.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Is value string?
+ */
+const tbIsString = (row, getValue) => {
+  return typeof getValue(row) === 'string'
+}
+
+//--------------------------------------------------------------------------------
+
+/*
+ * Convert string to date object using format.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row Row containing values.
+ * @param {string} format Format to use for parsing (FIXME: IGNORED UNTIL WE CAN LOAD 'moment').
+ * @param {function} getValue How to get desired value.
+ * @returns Date corresponding to string.
+ */
+const tbParseDate = (blockId, row, format, getValue) => {
+  const value = getValue(row)
+  tbAssert(typeof value === 'string',
+           `Expected string not ${typeof value}`)
+  return new Date(value)
+}
+
+/*
+ * Extract year from value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Year as number.
+ */
+const tbToYear = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getFullYear()
+}
+
+/**
+ * Extract month from value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Month as number.
+ */
+const tbToMonth = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getMonth() + 1 // normalize to 1-12 to be consistent with days of month
+}
+
+/**
+ * Extract day of month from value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Day of month as number.
+ */
+const tbToDay = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getDate()
+}
+
+/**
+ * Extract day of week from value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Day of month as number.
+ */
+const tbToWeekDay = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getDay()
+}
+
+/**
+ * Extract hours from date value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Hours portion of value.
+ */
+const tbToHours = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getHours()
+}
+
+/**
+ * Extract minutes from date value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Minutes portion of value.
+ */
+const tbToMinutes = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getMinutes()
+}
+
+/**
+ * Extract seconds from date value.
+ * @param {Object} row Row containing values.
+ * @param {function} getValue How to get desired value.
+ * @returns Seconds portion of value.
+ */
+const tbToSeconds = (row, getValue) => {
+  const value = getValue(row)
+  tbAssert(value instanceof Date,
+           `Expected date object not "${value}"`)
+  return value.getSeconds()
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Get a column's value from a row, failing if the column doesn't exist.
+ * @param {Object} row The row to look in.
+ * @param {string} column The field to look up.
+ * @returns The value.
+ */
+const tbGet = (blockId, row, column) => {
+  tbAssert(column in row,
+           `[block ${blockId}] no such column "${column}" (have [${Object.keys(row).join(',')}])`)
+  return row[column]
+}
+
+/**
+ * Add two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The sum.
+ */
+const tbAdd = (blockId, row, getLeft, getRight) => {
+  const left = tbAssertNumber(getLeft(row))
+  const right = tbAssertNumber(getRight(row))
+  return left + right
+}
+
+/**
+ * Divide two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The quotient.
+ */
+const tbDiv = (blockId, row, getLeft, getRight) => {
+  const left = tbAssertNumber(getLeft(row))
+  const right = tbAssertNumber(getRight(row))
+  return left / right
+}
+
+/**
+ * Calculate an exponent.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The exponentiated value.
+ */
+const tbExp = (blockId, row, getLeft, getRight) => {
+  const left = tbAssertNumber(getLeft(row))
+  const right = tbAssertNumber(getRight(row))
+  return left ** right
+}
+
+/**
+ * Find the remainder of two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The remainder.
+ */
+const tbMod = (blockId, row, getLeft, getRight) => {
+  const left = tbAssertNumber(getLeft(row))
+  const right = tbAssertNumber(getRight(row))
+  return left % right
+}
+
+/**
+ * Multiply two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The product.
+ */
+const tbMul = (blockId, row, getLeft, getRight) => {
+  const left = tbAssertNumber(getLeft(row))
+  const right = tbAssertNumber(getRight(row))
+  return left * right
+}
+
+/**
+ * Negate a value.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getValue How to get the value from the row.
+ * @returns The numerical negation.
+ */
+const tbNeg = (blockId, row, getValue) => {
+  const value = tbAssertNumber(getValue(row))
+  return - value
+}
+
+/**
+ * Subtract two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The difference.
+ */
+const tbSub = (blockId, row, getLeft, getRight) => {
+  const left = tbAssertNumber(getLeft(row))
+  const right = tbAssertNumber(getRight(row))
+  return left - right
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Logical conjunction of two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The conjunction.
+ */
+const tbAnd = (blockId, row, getLeft, getRight) => {
+  const left = tbToBoolean(row, getLeft)
+  const right = tbToBoolean(row, getRight)
+  return left && right
+}
+
+/**
+ * Logical negation of a value.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getValue How to get the value from the row.
+ * @returns The logical conjunction.
+ */
+const tbNot = (blockId, row, getValue) => {
+  const value = tbToLogical(getValue(row))
+  return ! value
+}
+
+/**
+ * Logical disjunction of two values.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The disjunction.
+ */
+const tbOr = (blockId, row, getLeft, getRight) => {
+  const left = tbToBoolean(row, getLeft)
+  const right = tbToBoolean(row, getRight)
+  return left || right
+}
+
+/**
+ * Choosing a value based on a logical condition.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getCond How to get the condition's value.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The left (right) value if the condition is true (false).
+ */
+const tbIfElse = (blockId, row, getCond, getLeft, getRight) => {
+  const cond = tbToBoolean(row, getCond)
+  return cond ? getLeft(row) : getRight(row)
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Strict greater than.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The comparison's result.
+ */
+const tbGt = (blockId, row, getLeft, getRight) => {
+  const left = getLeft(row)
+  const right = getRight(row)
+  tbTypeEqual(left, right)
+  return left > right
+}
+
+/**
+ * Greater than or equal.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The comparison's result.
+ */
+const tbGeq = (blockId, row, getLeft, getRight) => {
+  const left = getLeft(row)
+  const right = getRight(row)
+  tbTypeEqual(left, right)
+  return left >= right
+}
+
+/**
+ * Equality.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The comparison's result.
+ */
+const tbEq = (blockId, row, getLeft, getRight) => {
+  const left = getLeft(row)
+  const right = getRight(row)
+  tbTypeEqual(left, right)
+  return left === right
+}
+
+/**
+ * Inequality.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The comparison's result.
+ */
+const tbNeq = (blockId, row, getLeft, getRight) => {
+  const left = getLeft(row)
+  const right = getRight(row)
+  tbTypeEqual(left, right)
+  return left !== right
+}
+
+/**
+ * Less than or equal.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The comparison's result.
+ */
+const tbLeq = (blockId, row, getLeft, getRight) => {
+  const left = getLeft(row)
+  const right = getRight(row)
+  tbTypeEqual(left, right)
+  return left <= right
+}
+
+/**
+ * Strictly less than.
+ * @param {number} blockId The ID of the block.
+ * @param {Object} row The row to get values from.
+ * @param {function} getLeft How to get the left value from the row.
+ * @param {function} getRight How to get the right value from the row.
+ * @returns The comparison's result.
+ */
+const tbLt = (blockId, row, getLeft, getRight) => {
+  const left = getLeft(row)
+  const right = getRight(row)
+  tbTypeEqual(left, right)
+  return left < right
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Store a dataframe.
+ */
+class TidyBlocksDataFrame {
+
+  /**
+   * Construct a new dataframe.
+   * @param {Object[]} values The initial values (aliased).
+   */
+  constructor (values) {
+    this.data = values
+  }
+
+  //------------------------------------------------------------------------------
+
+  /**
+   * Filter rows, keeping those that pass a test.
+   * @param {function} op How to test rows.
+   * @returns A new dataframe.
+   */
+  filter (blockId, op) {
+    tbAssert(op, `[block ${blockId}] no operator for filter`)
+    const newData = this.data.filter(row => {
+      return op(row)
+    })
+    return new TidyBlocksDataFrame(newData)
+  }
+
+  /**
+   * Group by the values in a column, storing the result in a new _group_ column.
+   * @param {string} column The column that determines groups.
+   * @returns A new dataframe.
+   */
+  groupBy (blockId, column) {
+    tbAssert(column.length !== 0,
+             `[block ${blockId}] empty column name for grouping`)
+    const seen = new Map()
+    let groupId = 0
+    const grouped = this.data.map(row => {
+      row = {...row}
+      const value = tbGet(blockId, row, column)
+      if (! seen.has(value)) {
+        seen.set(value, groupId)
+        groupId += 1
+      }
+      row._group_ = seen.get(value)
+      return row
+    })
+    return new TidyBlocksDataFrame(grouped)
+  }
+
+  /**
+   * Create a new column by operating on existing columns.
+   * @param {string} newName New column's name.
+   * @param {function} op How to create new values from a row.
+   * @returns A new dataframe.
+   */
+  mutate (blockId, newName, op) {
+    tbAssert(newName,
+             `[block ${blockId}] empty new column name for mutate`)
+    tbAssert(op !== null,
+             `[block ${blockId}] no operator for mutate`)
+    const newData = this.data.map(row => {
+      const newRow = {...row}
+      newRow[newName] = op(row)
+      return newRow
+    })
+    return new TidyBlocksDataFrame(newData)
+  }
+
+  /**
+   * Select columns.
+   * @param {string[]} columns The names of the columns to keep.
+   * @returns A new dataframe.
+   */
+  select (blockId, columns) {
+    tbAssert(columns.length !== 0,
+             `[block ${blockId}] no columns specified for select`)
+    tbAssert(this.hasColumns(columns),
+             `[block ${blockId}] unknown column(s) [${columns}] in select`)
+    const newData = this.data.map(row => {
+      const result = {}
+      columns.forEach(key => {
+        result[key] = tbGet(blockId, row, key)
+      })
+      return result
+    })
+    return new TidyBlocksDataFrame(newData)
+  }
+
+  /**
+   * Sort data by values in specified columns.
+   * @param {string[]} columns Names of columns to sort by.
+   * @returns New data frame with sorted data.
+   */
+  sort (blockId, columns, reverse) {
+    tbAssert(columns.length !== 0,
+             `[block ${blockId}] no columns specified for sort`)
+    tbAssert(this.hasColumns(columns),
+             `[block ${blockId}] unknown column(s) [${columns}] in sort`)
+    const result = [...this.data]
+    result.sort((left, right) => {
+      return columns.reduce((soFar, col) => {
+        if (soFar !== 0) {
+          return soFar
+        }
+        if (left[col] < right[col]) {
+          return -1
+        }
+        if (left[col] > right[col]) {
+          return 1
+        }
+        return 0
+      }, 0)
+    })
+    if (reverse) {
+      result.reverse()
+    }
+    return new TidyBlocksDataFrame(result)
+  }
+
+  /**
+   * Replace internal dataframe with a summarized dataframe.
+   * @param {function} func Summarization function.
+   * @param {string} column Column to summarize.
+   * @return A new dataframe.
+   */
+  summarize (blockId, func, column) {
+    // Handle empty case.
+    if (this.data.length === 0) {
+      return new TidyBlocksDataFrame([])
+    }
+
+    // Check column access.
+    tbAssert(column,
+             `[block ${blockId}] no column specified for summarize`)
+    tbAssert(this.hasColumns(column),
+             `[block ${blockId}] unknown column(s) [${column}] in summarize`)
+
+    // Final data.
+    const result = []
+
+    // Aggregate the whole thing?
+    if (! this.hasColumns('_group_')) {
+      const values = this.getColumn(column)
+      const record = {}
+      record[column] = func(values)
+      result.push(record)
+    }
+
+    // Aggregate by groups
+    else {
+      // _group_ values in column by index.
+      const grouped = new Map()
+      this.data.forEach(row => {
+        if (grouped.has(row._group_)) {
+          grouped.get(row._group_).push(row[column])
+        }
+        else {
+          grouped.set(row._group_, [row[column]])
+        }
+      })
+
+      // Operate by group.
+      grouped.forEach((values, group) => {
+        const record = {}
+        record['_group_'] = group
+        record[column] = func(values)
+        result.push(record)
+      })
+    }
+
+    // Create new dataframe.
+    return new TidyBlocksDataFrame(result)
+  }
+
+  /**
+   * Remove grouping if present.
+   * @returns A new dataframe.
+   */
+  ungroup (blockId) {
+    tbAssert(this.hasColumns('_group_'),
+             `[block ${blockId}] cannot ungroup data that is not grouped`)
+    const newData = this.data.map(row => {
+      row = {...row}
+      delete row._group_
+      return row
+    })
+    return new TidyBlocksDataFrame(newData)
+  }
+
+  //------------------------------------------------------------------------------
+
+  /**
+   * Join two tables on equality between values in specified columns.
+   * @param {function} getDataFxn How to look up data by name.
+   * @param {string} leftTable Notification name of left table to join.
+   * @param {string} leftColumn Name of column from left table.
+   * @param {string} rightTable Notification name of right table to join.
+   * @param {string} rightColumn Name of column from right table.
+   * @returns A new dataframe.
+   */
+  join (getDataFxn, leftTableName, leftColumn, rightTableName, rightColumn) {
+
+    const _addFieldsExcept = (result, tableName, row, exceptName) => {
+      Object.keys(row)
+        .filter(key => (key != exceptName))
+        .forEach(key => {result[`${tableName}_${key}`] = row[key]})
+    }
+
+    const leftFrame = getDataFxn(leftTableName)
+    tbAssert(leftFrame.hasColumns(leftColumn),
+             `left table does not have column ${leftColumn}`)
+    const rightFrame = getDataFxn(rightTableName)
+    tbAssert(rightFrame.hasColumns(rightColumn),
+             `right table does not have column ${rightColumn}`)
+
+    const result = []
+    for (let leftRow of leftFrame.data) { 
+      for (let rightRow of rightFrame.data) { 
+        if (leftRow[leftColumn] === rightRow[rightColumn]) {
+          const row = {'_join_': leftRow[leftColumn]}
+          _addFieldsExcept(row, leftTableName, leftRow, leftColumn)
+          _addFieldsExcept(row, rightTableName, rightRow, rightColumn)
+          result.push(row)
+        }
+      }
+    } 
+
+    return new TidyBlocksDataFrame(result)
+  }
+
+  /**
+   * Notify the pipeline manager that this pipeline has completed so that downstream joins can run.
+   * Note that this function is called at the end of a pipeline, so it does not return 'this' to support method chaining.
+   * @param {function} notifyFxn Callback functon to do notification (to decouple this class from the manager).
+   * @param {string} name Name of this pipeline.
+   */
+  notify (notifyFxn, name) {
+    notifyFxn(name, this)
+  }
+
+  //------------------------------------------------------------------------------
+
+  /**
+   * Call a plotting function. This is in this class to support method chaining
+   * and to decouple this class from the real plotting functions so that tests
+   * will run.
+   * @param {object} environment Connection to the outside world.
+   * @param {object} spec Vega-Lite specification with empty 'values' (filled in here with actual data before plotting).
+   * @returns This object.
+   */
+  plot (environment, spec) {
+    environment.displayTable(this.data)
+    if (Object.keys(spec).length !== 0) {
+      spec.data.values = this.data
+      environment.displayPlot(spec)
+    }
+    return this
+  }
+
+  //------------------------------------------------------------------------------
+
+  /**
+   * Get a column as a JavaScript array.
+   * @param {string} name Name of column to get.
+   * @returns {Array} Column as JavaScript array.
+   */
+  getColumn (name) {
+    tbAssert(this.hasColumns(name),
+             `Table does not have column ${name}`)
+    return this.data.map(row => row[name])
+  }
+
+  /**
+   * Test whether the dataframe has the specified columns.
+   * @param {string[]} names Names of column to check for.
+   * @returns {Boolean} Are columns present?
+   */
+  hasColumns (names) {
+    if (this.data.length === 0) {
+      return false
+    }
+    if (typeof names === 'string') {
+      names = [names]
+    }
+    return names.every(n => (n in this.data[0]))
+  }
+
+  /**
+   * Convert columns to numeric values.
+   * @param {string[]} columns The names of the columns to convert.
+   * @returns This object.
+   */
+  toNumber (blockId, columns) {
+    this.data.forEach(row => {
+      columns.forEach(col => {
+        row[col] = parseFloat(tbGet(blockId, row, col))
+      })
+    })
+    return this
+  }
+
+  /**
+   * Convert to string for printing.
+   */
+  toString () {
+    const str = (row, i) => {
+      return '{'
+        + Object.keys(row).map(key => `${key}: ${row[key]}`).join(', ')
+        + '}'
+        + (this.groups === null ? '' : ` @ ${this.groups[i]}`)
+    }
+    return `= ${this.data.length} =\n`
+      + this.data.map((r, i) => str(r, i)).join('\n')
+  }
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Manage execution of all data pipelines.
+ */
+class TidyBlocksManagerClass {
+
+  /**
+   * Create manager.
+   */
+  constructor () {
+    this.reset()
+  }
+
+  /**
+   * Record a newly-created block and add the ID to its tooltip.
+   * @param {block} block Newly-created block.
+   */
+  addNewBlock (block) {
+    block.tbId = this.nextBlockId
+    block.tooltip = `[${block.tbId}] ${block.tooltip}`
+    this.blocks.set(this.nextBlockId, block)
+    this.nextBlockId += 1
+  }
+
+  /**
+   * Get the number of blocks that have been created (including ones that have
+   * now been deleted).
+   */
+  getNumBlocks () {
+    return this.blocks.size
+  }
+
+  /**
+   * Get a block by serial number.
+   * @param {number} blockId Serial number of block.
+   * @returns {block} The block or null.
+   */
+  getBlock (blockId) {
+    if (this.blocks.has(blockId)) {
+      return this.blocks.get(blockId)
+    }
+    return null
+  }
+
+  /**
+   * Get the output of a completed pipeline.
+   * @param {string} name Name of completed pipeline.
+   * @return TidyBlocksDataFrame.
+   */
+  getResult (name) {
+    return this.results.get(name)
+  }
+
+  /**
+   * Notify the manager that a named pipeline has finished running.
+   * This enqueues pipeline functions to run if their dependencies are satisfied.
+   * @param {string} name Name of the pipeline that just completed.
+   * @param {Object} dataFrame The TidyBlocksDataFrame produced by the pipeline.
+   */
+  notify (name, dataFrame) {
+    this.results.set(name, dataFrame)
+    this.waiting.forEach((dependencies, func) => {
+      dependencies.delete(name)
+      if (dependencies.size === 0) {
+        this.queue.push(func)
+      }
+    })
+  }
+
+  /**
+   * Register a new pipeline function with what it depends on and what it produces.
+   * @param {string[]} depends Names of things this pipeline depends on (if it starts with a join).
+   * @param {function} func Function encapsulating pipeline to run.
+   * @param {function} produces Name of this pipeline (used to trigger things waiting for it).
+   */
+  register (depends, func, produces) {
+    if (depends.length == 0) {
+      this.queue.push(func)
+    }
+    else {
+      this.waiting.set(func, new Set(depends))
+    }
+  }
+
+  /**
+   * Reset internal state.
+   */
+  reset () {
+    this.queue = []
+    this.waiting = new Map()
+    this.results = new Map()
+    this.blocks = new Map()
+    this.nextBlockId = 0
+  }
+
+  /**
+   * Run all pipelines in an order that respects dependencies.
+   * This depends on `notify` to add pipelines to the queue.
+   * @param {object} environment How to interact with the outside world.
+   */
+  run (environment) {
+    environment.displayError('') // clear legacy errors
+    try {
+      let code = environment.getCode()
+      if (! code.includes(TIDYBLOCKS_START)) {
+        throw new Error('pipeline does not have a valid start block')
+      }
+      code = fixCode(code)
+      eval(code)
+      while (this.queue.length > 0) {
+        const func = this.queue.shift()
+        func()
+      }
+    }
+    catch (err) {
+      environment.displayError(err.message)
+    }
+  }
+
+  /**
+   * Show the manager as a string for debugging.
+   */
+  toString () {
+    return `queue ${this.queue.length} waiting ${this.waiting.length} blocks ${this.blocks.size}`
+  }
+}
+
+//--------------------------------------------------------------------------------
+
+/**
+ * Find linear model for plotting.
+ * @param {number[]} values_x X-axis values.
+ * @param {number[]} values_y Y-axis values.
+ * @returns {number[]} Slope and intercept.
+ */
+const findLineByLeastSquares = (values_x, values_y) => {
+  const len = values_x.length
+  if (len != values_y.length) {
+    throw new Error('values_x and values_y have different lengths')
+  }
+
+  // Empty case.
+  if (len === 0) {
+    return [NaN, NaN]
+  }
+
+  // Calculate the sum for each of the parts necessary.
+  let x_sum = 0
+  let y_sum = 0
+  let xy_sum = 0
+  let xx_sum = 0
+  for (let i = 0; i<len; i++) {
+    const x = values_x[i]
+    const y = values_y[i]
+    x_sum += x
+    y_sum += y
+    xx_sum += x * x
+    xy_sum += x * y
+  }
+
+  // Calculate m and b for the line equation:
+  // y = x * m + b
+  var m = (len * xy_sum - x_sum * y_sum) / (len * xx_sum - x_sum * x_sum)
+  var b = (y_sum / len) - (m * x_sum) / len
+
+  // Solve for slope and intercept.
+  return [m, b]
+}
+
+/**
+ * Singleton instance of manager.
+ */
+const TidyBlocksManager = new TidyBlocksManagerClass()
+
+// Make this file require'able if running from the command line.
+if (typeof module !== 'undefined') {
+  module.exports = {
+    csv2TidyBlocksDataFrame,
+    registerPrefix,
+    registerSuffix,
+    TidyBlocksDataFrame,
+    TidyBlocksManager
+  }
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:23:53 GMT-0400 (Eastern Daylight Time) +
+ + + + + diff --git a/docs/tidyblocks_gui.js.html b/docs/tidyblocks_gui.js.html index c0e43194c..98883dd7e 100644 --- a/docs/tidyblocks_gui.js.html +++ b/docs/tidyblocks_gui.js.html @@ -36,6 +36,7 @@

Source: tidyblocks/gui.js

// Names of single-column fields in various blocks (for generating validators). const SINGLE_COLUMN_FIELDS = [ 'COLUMN', + 'FORMAT', 'LEFT_TABLE', 'LEFT_COLUMN', 'RIGHT_TABLE', @@ -51,6 +52,70 @@

Source: tidyblocks/gui.js

'MULTIPLE_COLUMNS' ] +//-------------------------------------------------------------------------------- + +/** + * Class to handle connections to the outside world. (A different class with + * the same methods is used for testing purposes.) + */ +class GuiEnvironment { + + constructor () { + } + + /** + * Get the code to run. + * @returns {string} The code to run. + */ + getCode () { + return Blockly.JavaScript.workspaceToCode(TidyBlocksWorkspace) + } + + /** + * Read CSV from a URL and parse to create TidyBlocks data frame. + * @param {string} url URL to read from. + */ + readCSV (url) { + const request = new XMLHttpRequest() + request.open('GET', url, false) + request.send(null) + + if (request.status !== 200) { + console.log(`ERROR: ${request.status}`) + return null + } + else { + return csv2TidyBlocksDataFrame(request.responseText, Papa.parse) + } + } + + /** + * Display a plot. + * @param {Object} spec Vega-Lite spec for plot with data filled in. + */ + displayPlot (spec) { + vegaEmbed('#plotOutput', spec, {}) + } + + /** + * Display a table (as HTML). + * @param {Object} table JSON array of uniform objects. + */ + displayTable (table) { + document.getElementById('dataOutput').innerHTML = json2table(table) + } + + /** + * Display an error. + * @param {string} error The message to display. + */ + displayError (error) { + document.getElementById('error').innerHTML = `<p>${error}</p>` + } +} + +//-------------------------------------------------------------------------------- + /** * Set the display property of the two input toggleable panes. * (Has to be done manually rather than in CSS because properties are being reset.) @@ -144,38 +209,13 @@

Source: tidyblocks/gui.js

} } -/** - * Callback for displaying a plot. - * @param {Object} spec Vega-Lite spec for plot with data filled in. - */ -const displayPlot = (spec) => { - vegaEmbed('#plotOutput', spec, {}) -} - -/** - * Callback for displaying a table as HTML. - * @param {Object} table JSON array of uniform objects. - */ -const displayTable = (table) => { - document.getElementById('dataOutput').innerHTML = json2table(table) -} - -/** - * Callback fro displaying an error online. - * @param {string} error The message to display. - */ -const displayError = (error) => { - console.log(error) // FIXME display in the GUI -} - /** * Run the code generated from the user's blocks. * Depends on the global TidyBlocksWorkspace variable. */ const runCode = () => { Blockly.JavaScript.INFINITE_LOOP_TRAP = null - TidyBlocksManager.run(() => Blockly.JavaScript.workspaceToCode(TidyBlocksWorkspace), - displayTable, displayPlot, displayError, readCSV) + TidyBlocksManager.run(new GuiEnvironment()) } /** @@ -209,30 +249,15 @@

Source: tidyblocks/gui.js

}) } -/** - * Read CSV from a URL and parse to create TidyBlocks data frame. - * @param {string} url URL to read from. - */ -const readCSV = (url) => { - const request = new XMLHttpRequest() - request.open('GET', url, false) - request.send(null) - - if (request.status !== 200) { - console.log(`ERROR: ${request.status}`) - return null - } - else { - return csv2TidyBlocksDataFrame(request.responseText, Papa.parse) - } -} - /** * Produce a human-friendly name for the type of a column. * @param value The value whose type is checked. * @returns The name of the type */ const colTypeName = (value) => { + if (value instanceof Date) { + return 'datetime' + } return typeof value } @@ -242,6 +267,9 @@

Source: tidyblocks/gui.js

* @param {JSON} json JSON object to convert to table. */ const json2table = (json) => { + if (json.length === 0) { + return '<p>empty</p>' + } const cols = Object.keys(json[0]) const headerRow = '<tr>' + cols.map(c => `<th>${c}</th>`).join('') + '</tr>' const typeRow = '<tr>' + cols.map(c => `<th>${colTypeName(json[0][c])}</th>`).join('') + '</tr>' @@ -274,13 +302,13 @@

Source: tidyblocks/gui.js


- Documentation generated by JSDoc 3.6.3 on Thu Sep 05 2019 20:42:06 GMT-0400 (Eastern Daylight Time) + Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:03 GMT-0400 (Eastern Daylight Time)
diff --git a/docs/tidyblocks_tidyblocks.js.html b/docs/tidyblocks_tidyblocks.js.html index 713452095..82967a7f9 100644 --- a/docs/tidyblocks_tidyblocks.js.html +++ b/docs/tidyblocks_tidyblocks.js.html @@ -27,9 +27,10 @@

Source: tidyblocks/tidyblocks.js

/**
- * Terminator for well-formed pipelines.
+ * Prefix and suffix for well-formed pipelines.
  */
-const TERMINATOR = '// terminated'
+const TIDYBLOCKS_START = '/* tidyblocks start */'
+const TIDYBLOCKS_END = '/* tidyblocks end */'
 
 /**
  * Turn block of CSV text into TidyBlocksDataFrame. The parser argument should be Papa.parse;
@@ -89,7 +90,7 @@ 

Source: tidyblocks/tidyblocks.js

* @returns {string} Text to insert into generated code. */ const registerPrefix = (fill) => { - return `TidyBlocksManager.register([${fill}], () => {` + return `${TIDYBLOCKS_START} TidyBlocksManager.register([${fill}], () => {` } /** @@ -98,7 +99,7 @@

Source: tidyblocks/tidyblocks.js

* @returns {string} Text to insert into generated code. */ const registerSuffix = (fill) => { - return `}, [${fill}]) ${TERMINATOR}` + return `}, [${fill}]) ${TIDYBLOCKS_END}` } /** @@ -106,9 +107,9 @@

Source: tidyblocks/tidyblocks.js

* @param {string} code Pipeline code to be terminated if necessary. */ const fixCode = (code) => { - if (! code.endsWith(TERMINATOR)) { + if (! code.endsWith(TIDYBLOCKS_END)) { const suffix = registerSuffix('') - code += `.plot(displayTable, null, '#plotOutput', {}) ${suffix}` + code += `.plot(environment, {}) ${suffix}` } return code } @@ -131,7 +132,7 @@

Source: tidyblocks/tidyblocks.js

* @param value What to check. * @returns The input value if it passes the test. */ -const tbIsNumber = (value) => { +const tbAssertNumber = (value) => { tbAssert(typeof value === 'number', `Value ${value} is not a number`) return value @@ -243,21 +244,38 @@

Source: tidyblocks/tidyblocks.js

/** * Convert row value to Boolean. + * @param {number{ blockId which block this is. * @param {Object} row Row containing values. * @param {function} getValue How to get desired value. * @returns Boolean value. */ -const tbToBoolean = (row, getValue) => { +const tbToBoolean = (blockId, row, getValue) => { return getValue(row) ? true : false } +/** + * Convert row value to datetime. + * @param {number{ blockId which block this is. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Date object. + */ +const tbToDatetime = (blockId, row, getValue) => { + const value = getValue(row) + const result = new Date(value) + tbAssert(!isNaN(result), + `[block ${blockId}] cannot convert "${value}" to date`) + return result +} + /** * Convert row value to number. + * @param {number{ blockId which block this is. * @param {Object} row Row containing values. * @param {function} getValue How to get desired value. * @returns Numeric value. */ -const tbToNumber = (row, getValue) => { +const tbToNumber = (blockId, row, getValue) => { const value = getValue(row) if (typeof value == 'boolean') { return value ? 1 : 0 @@ -270,11 +288,12 @@

Source: tidyblocks/tidyblocks.js

/** * Convert row value to string. + * @param {number{ blockId which block this is. * @param {Object} row Row containing values. * @param {function} getValue How to get desired value. * @returns String value. */ -const tbToString = (row, getValue) => { +const tbToString = (blockId, row, getValue) => { const value = getValue(row) if (typeof value == 'string') { return value @@ -284,104 +303,251 @@

Source: tidyblocks/tidyblocks.js

//-------------------------------------------------------------------------------- +/** + * Check if value is Boolean. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Is value Boolean? + */ +const tbIsBoolean = (row, getValue) => { + return typeof getValue(row) === 'boolean' +} + +/** + * Check if value is number. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Is value numeric? + */ +const tbIsNumber = (row, getValue) => { + return typeof getValue(row) === 'number' +} + +/** + * Check if value is string. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Is value string? + */ +const tbIsString = (row, getValue) => { + return typeof getValue(row) === 'string' +} + +//-------------------------------------------------------------------------------- + +/* + * Convert string to date object using format. + * @param {number} blockId The ID of the block. + * @param {Object} row Row containing values. + * @param {string} format Format to use for parsing (FIXME: IGNORED UNTIL WE CAN LOAD 'moment'). + * @param {function} getValue How to get desired value. + * @returns Date corresponding to string. + */ +const tbParseDate = (blockId, row, format, getValue) => { + const value = getValue(row) + tbAssert(typeof value === 'string', + `Expected string not ${typeof value}`) + return new Date(value) +} + +/* + * Extract year from value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Year as number. + */ +const tbToYear = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getFullYear() +} + +/** + * Extract month from value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Month as number. + */ +const tbToMonth = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getMonth() + 1 // normalize to 1-12 to be consistent with days of month +} + +/** + * Extract day of month from value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Day of month as number. + */ +const tbToDay = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getDate() +} + +/** + * Extract day of week from value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Day of month as number. + */ +const tbToWeekDay = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getDay() +} + +/** + * Extract hours from date value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Hours portion of value. + */ +const tbToHours = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getHours() +} + +/** + * Extract minutes from date value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Minutes portion of value. + */ +const tbToMinutes = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getMinutes() +} + +/** + * Extract seconds from date value. + * @param {Object} row Row containing values. + * @param {function} getValue How to get desired value. + * @returns Seconds portion of value. + */ +const tbToSeconds = (row, getValue) => { + const value = getValue(row) + tbAssert(value instanceof Date, + `Expected date object not "${value}"`) + return value.getSeconds() +} + +//-------------------------------------------------------------------------------- + /** * Get a column's value from a row, failing if the column doesn't exist. * @param {Object} row The row to look in. - * @param {string} key The field to look up. + * @param {string} column The field to look up. * @returns The value. */ -const tbGet = (row, key) => { - tbAssert(key in row, - `Key ${key} not in row ${Object.keys(row).join(',')}`) - return row[key] +const tbGet = (blockId, row, column) => { + tbAssert(column in row, + `[block ${blockId}] no such column "${column}" (have [${Object.keys(row).join(',')}])`) + return row[column] } /** * Add two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The sum. */ -const tbAdd = (row, getLeft, getRight) => { - const left = tbIsNumber(getLeft(row)) - const right = tbIsNumber(getRight(row)) +const tbAdd = (blockId, row, getLeft, getRight) => { + const left = tbAssertNumber(getLeft(row)) + const right = tbAssertNumber(getRight(row)) return left + right } /** * Divide two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The quotient. */ -const tbDiv = (row, getLeft, getRight) => { - const left = tbIsNumber(getLeft(row)) - const right = tbIsNumber(getRight(row)) +const tbDiv = (blockId, row, getLeft, getRight) => { + const left = tbAssertNumber(getLeft(row)) + const right = tbAssertNumber(getRight(row)) return left / right } /** * Calculate an exponent. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The exponentiated value. */ -const tbExp = (row, getLeft, getRight) => { - const left = tbIsNumber(getLeft(row)) - const right = tbIsNumber(getRight(row)) +const tbExp = (blockId, row, getLeft, getRight) => { + const left = tbAssertNumber(getLeft(row)) + const right = tbAssertNumber(getRight(row)) return left ** right } /** * Find the remainder of two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The remainder. */ -const tbMod = (row, getLeft, getRight) => { - const left = tbIsNumber(getLeft(row)) - const right = tbIsNumber(getRight(row)) - return left * right +const tbMod = (blockId, row, getLeft, getRight) => { + const left = tbAssertNumber(getLeft(row)) + const right = tbAssertNumber(getRight(row)) + return left % right } /** * Multiply two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The product. */ -const tbMul = (row, getLeft, getRight) => { - const left = tbIsNumber(getLeft(row)) - const right = tbIsNumber(getRight(row)) - return left % right +const tbMul = (blockId, row, getLeft, getRight) => { + const left = tbAssertNumber(getLeft(row)) + const right = tbAssertNumber(getRight(row)) + return left * right } /** * Negate a value. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getValue How to get the value from the row. * @returns The numerical negation. */ -const tbNeg = (row, getValue) => { - const value = tbIsNumber(getValue(row)) +const tbNeg = (blockId, row, getValue) => { + const value = tbAssertNumber(getValue(row)) return - value } /** * Subtract two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The difference. */ -const tbSub = (row, getLeft, getRight) => { - const left = tbIsNumber(getLeft(row)) - const right = tbIsNumber(getRight(row)) +const tbSub = (blockId, row, getLeft, getRight) => { + const left = tbAssertNumber(getLeft(row)) + const right = tbAssertNumber(getRight(row)) return left - right } @@ -389,12 +555,13 @@

Source: tidyblocks/tidyblocks.js

/** * Logical conjunction of two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The conjunction. */ -const tbAnd = (row, getLeft, getRight) => { +const tbAnd = (blockId, row, getLeft, getRight) => { const left = tbToBoolean(row, getLeft) const right = tbToBoolean(row, getRight) return left && right @@ -402,38 +569,55 @@

Source: tidyblocks/tidyblocks.js

/** * Logical negation of a value. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getValue How to get the value from the row. * @returns The logical conjunction. */ -const tbNot = (row, getValue) => { +const tbNot = (blockId, row, getValue) => { const value = tbToLogical(getValue(row)) return ! value } /** * Logical disjunction of two values. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The disjunction. */ -const tbOr = (row, getLeft, getRight) => { +const tbOr = (blockId, row, getLeft, getRight) => { const left = tbToBoolean(row, getLeft) const right = tbToBoolean(row, getRight) return left || right } +/** + * Choosing a value based on a logical condition. + * @param {number} blockId The ID of the block. + * @param {Object} row The row to get values from. + * @param {function} getCond How to get the condition's value. + * @param {function} getLeft How to get the left value from the row. + * @param {function} getRight How to get the right value from the row. + * @returns The left (right) value if the condition is true (false). + */ +const tbIfElse = (blockId, row, getCond, getLeft, getRight) => { + const cond = tbToBoolean(row, getCond) + return cond ? getLeft(row) : getRight(row) +} + //-------------------------------------------------------------------------------- /** * Strict greater than. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbGt = (row, getLeft, getRight) => { +const tbGt = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -442,12 +626,13 @@

Source: tidyblocks/tidyblocks.js

/** * Greater than or equal. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbGeq = (row, getLeft, getRight) => { +const tbGeq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -456,12 +641,13 @@

Source: tidyblocks/tidyblocks.js

/** * Equality. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbEq = (row, getLeft, getRight) => { +const tbEq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -470,12 +656,13 @@

Source: tidyblocks/tidyblocks.js

/** * Inequality. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbNeq = (row, getLeft, getRight) => { +const tbNeq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -484,12 +671,13 @@

Source: tidyblocks/tidyblocks.js

/** * Less than or equal. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbLeq = (row, getLeft, getRight) => { +const tbLeq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -498,12 +686,13 @@

Source: tidyblocks/tidyblocks.js

/** * Strictly less than. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbLt = (row, getLeft, getRight) => { +const tbLt = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -532,7 +721,8 @@

Source: tidyblocks/tidyblocks.js

* @param {function} op How to test rows. * @returns A new dataframe. */ - filter (op) { + filter (blockId, op) { + tbAssert(op, `[block ${blockId}] no operator for filter`) const newData = this.data.filter(row => { return op(row) }) @@ -544,12 +734,14 @@

Source: tidyblocks/tidyblocks.js

* @param {string} column The column that determines groups. * @returns A new dataframe. */ - groupBy (column) { + groupBy (blockId, column) { + tbAssert(column.length !== 0, + `[block ${blockId}] empty column name for grouping`) const seen = new Map() let groupId = 0 const grouped = this.data.map(row => { row = {...row} - const value = tbGet(row, column) + const value = tbGet(blockId, row, column) if (! seen.has(value)) { seen.set(value, groupId) groupId += 1 @@ -566,7 +758,11 @@

Source: tidyblocks/tidyblocks.js

* @param {function} op How to create new values from a row. * @returns A new dataframe. */ - mutate (newName, op) { + mutate (blockId, newName, op) { + tbAssert(newName, + `[block ${blockId}] empty new column name for mutate`) + tbAssert(op !== null, + `[block ${blockId}] no operator for mutate`) const newData = this.data.map(row => { const newRow = {...row} newRow[newName] = op(row) @@ -575,25 +771,20 @@

Source: tidyblocks/tidyblocks.js

return new TidyBlocksDataFrame(newData) } - /** - * Reverse the order of rows. - */ - reverse () { - const result = [...this.data] - result.reverse() - return new TidyBlocksDataFrame(result) - } - /** * Select columns. * @param {string[]} columns The names of the columns to keep. * @returns A new dataframe. */ - select (columns) { + select (blockId, columns) { + tbAssert(columns.length !== 0, + `[block ${blockId}] no columns specified for select`) + tbAssert(this.hasColumns(columns), + `[block ${blockId}] unknown column(s) [${columns}] in select`) const newData = this.data.map(row => { const result = {} columns.forEach(key => { - result[key] = tbGet(row, key) + result[key] = tbGet(blockId, row, key) }) return result }) @@ -605,9 +796,11 @@

Source: tidyblocks/tidyblocks.js

* @param {string[]} columns Names of columns to sort by. * @returns New data frame with sorted data. */ - sort (columns) { - columns.forEach(col => tbAssert(this.hasColumn(col), - `No such column ${col}`)) + sort (blockId, columns, reverse) { + tbAssert(columns.length !== 0, + `[block ${blockId}] no columns specified for sort`) + tbAssert(this.hasColumns(columns), + `[block ${blockId}] unknown column(s) [${columns}] in sort`) const result = [...this.data] result.sort((left, right) => { return columns.reduce((soFar, col) => { @@ -623,6 +816,9 @@

Source: tidyblocks/tidyblocks.js

return 0 }, 0) }) + if (reverse) { + result.reverse() + } return new TidyBlocksDataFrame(result) } @@ -632,11 +828,23 @@

Source: tidyblocks/tidyblocks.js

* @param {string} column Column to summarize. * @return A new dataframe. */ - summarize (func, column) { + summarize (blockId, func, column) { + // Handle empty case. + if (this.data.length === 0) { + return new TidyBlocksDataFrame([]) + } + + // Check column access. + tbAssert(column, + `[block ${blockId}] no column specified for summarize`) + tbAssert(this.hasColumns(column), + `[block ${blockId}] unknown column(s) [${column}] in summarize`) + + // Final data. const result = [] // Aggregate the whole thing? - if (! this.hasColumn('_group_')) { + if (! this.hasColumns('_group_')) { const values = this.getColumn(column) const record = {} record[column] = func(values) @@ -673,9 +881,9 @@

Source: tidyblocks/tidyblocks.js

* Remove grouping if present. * @returns A new dataframe. */ - ungroup () { - tbAssert(this.hasColumn('_group_'), - 'Cannot ungroup data that is not grouped') + ungroup (blockId) { + tbAssert(this.hasColumns('_group_'), + `[block ${blockId}] cannot ungroup data that is not grouped`) const newData = this.data.map(row => { row = {...row} delete row._group_ @@ -704,10 +912,10 @@

Source: tidyblocks/tidyblocks.js

} const leftFrame = getDataFxn(leftTableName) - tbAssert(leftFrame.hasColumn(leftColumn), + tbAssert(leftFrame.hasColumns(leftColumn), `left table does not have column ${leftColumn}`) const rightFrame = getDataFxn(rightTableName) - tbAssert(rightFrame.hasColumn(rightColumn), + tbAssert(rightFrame.hasColumns(rightColumn), `right table does not have column ${rightColumn}`) const result = [] @@ -741,18 +949,15 @@

Source: tidyblocks/tidyblocks.js

* Call a plotting function. This is in this class to support method chaining * and to decouple this class from the real plotting functions so that tests * will run. - * @param {function} tableFxn Callback to display table as table. - * @param {function} plotFxn Callback to display table graphically. + * @param {object} environment Connection to the outside world. * @param {object} spec Vega-Lite specification with empty 'values' (filled in here with actual data before plotting). * @returns This object. */ - plot (tableFxn, plotFxn, spec) { - if (tableFxn !== null) { - tableFxn(this.data) - } - if (plotFxn !== null) { + plot (environment, spec) { + environment.displayTable(this.data) + if (Object.keys(spec).length !== 0) { spec.data.values = this.data - plotFxn(spec) + environment.displayPlot(spec) } return this } @@ -765,18 +970,24 @@

Source: tidyblocks/tidyblocks.js

* @returns {Array} Column as JavaScript array. */ getColumn (name) { - tbAssert(this.hasColumn(name), + tbAssert(this.hasColumns(name), `Table does not have column ${name}`) return this.data.map(row => row[name]) } /** - * Test whether the dataframe has the specified column. - * @param {string} name Name of column to check for. - * @returns {Boolean} Is column present? + * Test whether the dataframe has the specified columns. + * @param {string[]} names Names of column to check for. + * @returns {Boolean} Are columns present? */ - hasColumn (name) { - return name in this.data[0] + hasColumns (names) { + if (this.data.length === 0) { + return false + } + if (typeof names === 'string') { + names = [names] + } + return names.every(n => (n in this.data[0])) } /** @@ -784,10 +995,10 @@

Source: tidyblocks/tidyblocks.js

* @param {string[]} columns The names of the columns to convert. * @returns This object. */ - toNumber (columns) { + toNumber (blockId, columns) { this.data.forEach(row => { columns.forEach(col => { - row[col] = parseFloat(tbGet(row, col)) + row[col] = parseFloat(tbGet(blockId, row, col)) }) }) return this @@ -907,15 +1118,15 @@

Source: tidyblocks/tidyblocks.js

/** * Run all pipelines in an order that respects dependencies. * This depends on `notify` to add pipelines to the queue. - * @param {function} getCode How to get the code to run. - * @param {function} displayTable How to display a table (used in 'eval'). - * @param {function} displayPlot How to display a plot (used in 'eval'). - * @param {function} displayError How to display an error (used in 'eval' and here). - * @param {function} readCSV How to read a CSV file (used in 'eval'). + * @param {object} environment How to interact with the outside world. */ - run (getCode, displayTable, displayPlot, displayError, readCSV) { + run (environment) { + environment.displayError('') // clear legacy errors try { - let code = getCode() + let code = environment.getCode() + if (! code.includes(TIDYBLOCKS_START)) { + throw new Error('pipeline does not have a valid start block') + } code = fixCode(code) eval(code) while (this.queue.length > 0) { @@ -924,7 +1135,7 @@

Source: tidyblocks/tidyblocks.js

} } catch (err) { - displayError(err) + environment.displayError(err.message) } } @@ -1003,13 +1214,13 @@

Source: tidyblocks/tidyblocks.js


- Documentation generated by JSDoc 3.6.3 on Thu Sep 05 2019 20:42:06 GMT-0400 (Eastern Daylight Time) + Documentation generated by JSDoc 3.6.3 on Thu Sep 12 2019 16:24:03 GMT-0400 (Eastern Daylight Time)
diff --git a/package.json b/package.json index 8f328fd79..dad67cdbf 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "parcel build index.html", "dev": "parcel index.html", - "jsdoc": "jsdoc --destination docs --readme README.md tidyblocks/*.js tests/*.js", + "jsdoc": "jsdoc --destination docs --readme README.md tidyblocks/*.js test/*.js", "lint": "eslint blocks/*.js tidyblocks/*.js generators/js/*.js tests/*.js", "test": "mocha", "coverage": "nyc --reporter=html --reporter=text mocha" diff --git a/tidyblocks/tidyblocks.js b/tidyblocks/tidyblocks.js index 7123aa175..8c1b85afc 100644 --- a/tidyblocks/tidyblocks.js +++ b/tidyblocks/tidyblocks.js @@ -309,13 +309,13 @@ const tbIsString = (row, getValue) => { /* * Convert string to date object using format. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row Row containing values. * @param {string} format Format to use for parsing (FIXME: IGNORED UNTIL WE CAN LOAD 'moment'). * @param {function} getValue How to get desired value. * @returns Date corresponding to string. */ -const tbParseDate = (rowId, row, format, getValue) => { +const tbParseDate = (blockId, row, format, getValue) => { const value = getValue(row) tbAssert(typeof value === 'string', `Expected string not ${typeof value}`) @@ -429,13 +429,13 @@ const tbGet = (blockId, row, column) => { /** * Add two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The sum. */ -const tbAdd = (rowId, row, getLeft, getRight) => { +const tbAdd = (blockId, row, getLeft, getRight) => { const left = tbAssertNumber(getLeft(row)) const right = tbAssertNumber(getRight(row)) return left + right @@ -443,13 +443,13 @@ const tbAdd = (rowId, row, getLeft, getRight) => { /** * Divide two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The quotient. */ -const tbDiv = (rowId, row, getLeft, getRight) => { +const tbDiv = (blockId, row, getLeft, getRight) => { const left = tbAssertNumber(getLeft(row)) const right = tbAssertNumber(getRight(row)) return left / right @@ -457,13 +457,13 @@ const tbDiv = (rowId, row, getLeft, getRight) => { /** * Calculate an exponent. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The exponentiated value. */ -const tbExp = (rowId, row, getLeft, getRight) => { +const tbExp = (blockId, row, getLeft, getRight) => { const left = tbAssertNumber(getLeft(row)) const right = tbAssertNumber(getRight(row)) return left ** right @@ -471,13 +471,13 @@ const tbExp = (rowId, row, getLeft, getRight) => { /** * Find the remainder of two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The remainder. */ -const tbMod = (rowId, row, getLeft, getRight) => { +const tbMod = (blockId, row, getLeft, getRight) => { const left = tbAssertNumber(getLeft(row)) const right = tbAssertNumber(getRight(row)) return left % right @@ -485,13 +485,13 @@ const tbMod = (rowId, row, getLeft, getRight) => { /** * Multiply two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The product. */ -const tbMul = (rowId, row, getLeft, getRight) => { +const tbMul = (blockId, row, getLeft, getRight) => { const left = tbAssertNumber(getLeft(row)) const right = tbAssertNumber(getRight(row)) return left * right @@ -499,25 +499,25 @@ const tbMul = (rowId, row, getLeft, getRight) => { /** * Negate a value. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getValue How to get the value from the row. * @returns The numerical negation. */ -const tbNeg = (rowId, row, getValue) => { +const tbNeg = (blockId, row, getValue) => { const value = tbAssertNumber(getValue(row)) return - value } /** * Subtract two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The difference. */ -const tbSub = (rowId, row, getLeft, getRight) => { +const tbSub = (blockId, row, getLeft, getRight) => { const left = tbAssertNumber(getLeft(row)) const right = tbAssertNumber(getRight(row)) return left - right @@ -527,13 +527,13 @@ const tbSub = (rowId, row, getLeft, getRight) => { /** * Logical conjunction of two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The conjunction. */ -const tbAnd = (rowId, row, getLeft, getRight) => { +const tbAnd = (blockId, row, getLeft, getRight) => { const left = tbToBoolean(row, getLeft) const right = tbToBoolean(row, getRight) return left && right @@ -541,25 +541,25 @@ const tbAnd = (rowId, row, getLeft, getRight) => { /** * Logical negation of a value. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getValue How to get the value from the row. * @returns The logical conjunction. */ -const tbNot = (rowId, row, getValue) => { +const tbNot = (blockId, row, getValue) => { const value = tbToLogical(getValue(row)) return ! value } /** * Logical disjunction of two values. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The disjunction. */ -const tbOr = (rowId, row, getLeft, getRight) => { +const tbOr = (blockId, row, getLeft, getRight) => { const left = tbToBoolean(row, getLeft) const right = tbToBoolean(row, getRight) return left || right @@ -567,14 +567,14 @@ const tbOr = (rowId, row, getLeft, getRight) => { /** * Choosing a value based on a logical condition. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getCond How to get the condition's value. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The left (right) value if the condition is true (false). */ -const tbIfElse = (rowId, row, getCond, getLeft, getRight) => { +const tbIfElse = (blockId, row, getCond, getLeft, getRight) => { const cond = tbToBoolean(row, getCond) return cond ? getLeft(row) : getRight(row) } @@ -583,13 +583,13 @@ const tbIfElse = (rowId, row, getCond, getLeft, getRight) => { /** * Strict greater than. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbGt = (rowId, row, getLeft, getRight) => { +const tbGt = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -598,13 +598,13 @@ const tbGt = (rowId, row, getLeft, getRight) => { /** * Greater than or equal. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbGeq = (rowId, row, getLeft, getRight) => { +const tbGeq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -613,13 +613,13 @@ const tbGeq = (rowId, row, getLeft, getRight) => { /** * Equality. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbEq = (rowId, row, getLeft, getRight) => { +const tbEq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -628,13 +628,13 @@ const tbEq = (rowId, row, getLeft, getRight) => { /** * Inequality. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbNeq = (rowId, row, getLeft, getRight) => { +const tbNeq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -643,13 +643,13 @@ const tbNeq = (rowId, row, getLeft, getRight) => { /** * Less than or equal. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbLeq = (rowId, row, getLeft, getRight) => { +const tbLeq = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right) @@ -658,13 +658,13 @@ const tbLeq = (rowId, row, getLeft, getRight) => { /** * Strictly less than. - * @param {number} rowId The ID of the block. + * @param {number} blockId The ID of the block. * @param {Object} row The row to get values from. * @param {function} getLeft How to get the left value from the row. * @param {function} getRight How to get the right value from the row. * @returns The comparison's result. */ -const tbLt = (rowId, row, getLeft, getRight) => { +const tbLt = (blockId, row, getLeft, getRight) => { const left = getLeft(row) const right = getRight(row) tbTypeEqual(left, right)
NameTypeHow to get the left value from the row.
Description
getRightvalues -function +Array @@ -6521,7 +7492,7 @@
Parameters:
-
How to get the right value from the row.The values to be searched.
valueblockId + +number + + + What to check.The ID of the block.
row + + +Object + + + + The row to get values from.
getLeft + + +function + + + + How to get the left value from the row.
getRight + + +function + + + + How to get the right value from the row.
blockId + + +number + + + + The ID of the block.
row
rowblockId -Object +number @@ -7003,20 +8083,20 @@
Parameters:
-
The row to get values from.The ID of the block.
getLeftrow -function +Object @@ -7026,14 +8106,14 @@
Parameters:
-
How to get the left value from the row.The row to get values from.
getRightgetValue @@ -7049,7 +8129,7 @@
Parameters:
-
How to get the right value from the row.How to get the value from the row.
valuesblockId -Array +number @@ -7196,7 +8276,76 @@
Parameters:
-
The values to be searched.The ID of the block.
row + + +Object + + + + The row to get values from.
getLeft + + +function + + + + How to get the left value from the row.
getRight + + +function + + + + How to get the right value from the row.
valuesblockId -Array +number @@ -7355,7 +8492,53 @@
Parameters:
-
The values to be averaged.The ID of the block.
row + + +Object + + + + The row to get values from.
getValue + + +function + + + + How to get the value from the row.
valuesblockId -Array +number @@ -7514,7 +8685,76 @@
Parameters:
-
The values to be searched.The ID of the block.
row + + +Object + + + + The row to get values from.
getLeft + + +function + + + + How to get the left value from the row.
getRight + + +function + + + + How to get the right value from the row.
The values to be searched.The values to be summarized.
blockId + + +number + + + + The ID of the block.
row
row - - -Object - - - - The row to get values from.
getLeft - - -function - - - - How to get the left value from the row.
getRightvalues -function +Array @@ -8071,7 +9276,7 @@
Parameters:
-
How to get the right value from the row.The values to be added.
{number{ + + blockId which block this is.
rowThe row to get values from.Row containing values.
How to get the value from the row.How to get desired value.
row{number{ - -Object - - - The row to get values from.blockId which block this is.
getLeftrow -function +Object @@ -8411,14 +9641,14 @@
Parameters:
-
How to get the left value from the row.Row containing values.
getRightgetValue @@ -8434,7 +9664,7 @@
Parameters:
-
How to get the right value from the row.How to get desired value.
The row to get values from.Row containing values.
How to get the value from the row.How to get desired value.
The row to get values from.
getLeft - - -function - - - - How to get the left value from the row.Row containing values.
getRightgetValue @@ -8797,7 +10004,7 @@
Parameters:
-
How to get the right value from the row.How to get desired value.
valuesrow -Array +Object @@ -8944,7 +10151,30 @@
Parameters:
-
The values to be summarized.Row containing values.
getValue + + +function + + + + How to get desired value.
The row to get values from.
getLeft - - -function - - - - How to get the left value from the row.Row containing values.
getRightgetValue @@ -9149,7 +10344,7 @@
Parameters:
-
How to get the right value from the row.How to get desired value.
values{number{ + + blockId which block this is.
row -Array +Object @@ -9296,7 +10509,30 @@
Parameters:
-
The values to be added.Row containing values.
getValue + + +function + + + + How to get desired value.
{number{ + + blockId which block this is.
row