├── inst ├── www │ ├── styles │ │ ├── default │ │ │ └── default.css │ │ ├── loading.css │ │ ├── parsleyjs │ │ │ └── parsley.scss │ │ ├── viewercontrols.scss │ │ ├── jqModal │ │ │ └── jqModal.scss │ │ ├── vars.scss │ │ └── mixins.scss │ ├── partials │ │ ├── iframe.htm │ │ ├── _404.htm │ │ ├── dialogs │ │ │ ├── templates │ │ │ │ ├── newProfileVariable.tpl │ │ │ │ ├── viewerDataDownload.tpl │ │ │ │ ├── profileVariables.tpl │ │ │ │ ├── executionOrderDetails.tpl │ │ │ │ ├── viewerDataUpload.tpl │ │ │ │ └── viewerProfileVariables.tpl │ │ │ ├── _confirmDialog.htm │ │ │ ├── _styleEditorDialog.htm │ │ │ ├── _siteSettings.htm │ │ │ ├── _designerSettings.htm │ │ │ ├── _viewerDataUpload.htm │ │ │ ├── _profileSettings.htm │ │ │ ├── _viewerProfileSettings.htm │ │ │ ├── _controlSettings.htm │ │ │ ├── _viewerDataDownload.htm │ │ │ ├── _executionOrderDialog.htm │ │ │ ├── _dataSourceSettings.htm │ │ │ ├── _timerSettings.htm │ │ │ ├── _pageSettings.htm │ │ │ ├── _addPage.htm │ │ │ └── _formBuilder.htm │ │ └── viewer.htm │ ├── img │ │ └── spinner.gif │ ├── js │ │ ├── ui │ │ │ ├── controls │ │ │ │ ├── templates │ │ │ │ │ ├── interactivePlot.tpl │ │ │ │ │ ├── htmlWidget.tpl │ │ │ │ │ ├── profileConfigurator.tpl │ │ │ │ │ ├── actionButton.tpl │ │ │ │ │ ├── image.tpl │ │ │ │ │ ├── rText.tpl │ │ │ │ │ ├── rPrint.tpl │ │ │ │ │ ├── dataDownload.tpl │ │ │ │ │ ├── iframe.tpl │ │ │ │ │ ├── dataUpload.tpl │ │ │ │ │ ├── message.tpl │ │ │ │ │ ├── rPlot.tpl │ │ │ │ │ ├── spinner.tpl │ │ │ │ │ ├── leaflet.tpl │ │ │ │ │ └── dataTable.tpl │ │ │ │ ├── child │ │ │ │ │ ├── templates │ │ │ │ │ │ ├── submitButton.tpl │ │ │ │ │ │ ├── heading.tpl │ │ │ │ │ │ ├── textField.tpl │ │ │ │ │ │ ├── datePicker.tpl │ │ │ │ │ │ ├── checkboxList.tpl │ │ │ │ │ │ ├── radioButtonGroup.tpl │ │ │ │ │ │ ├── dropdown.tpl │ │ │ │ │ │ ├── slider.tpl │ │ │ │ │ │ ├── multiSelect.tpl │ │ │ │ │ │ └── dateRange.tpl │ │ │ │ │ ├── separator.js │ │ │ │ │ ├── submitButton.js │ │ │ │ │ ├── datePicker.js │ │ │ │ │ ├── textField.js │ │ │ │ │ ├── heading.js │ │ │ │ │ ├── dropdown.js │ │ │ │ │ ├── checkboxList.js │ │ │ │ │ ├── radioButtonGroup.js │ │ │ │ │ ├── multiSelect.js │ │ │ │ │ └── dateRange.js │ │ │ │ ├── images │ │ │ │ │ ├── layers.png │ │ │ │ │ ├── layers-2x.png │ │ │ │ │ ├── marker-icon.png │ │ │ │ │ ├── marker-icon-2x.png │ │ │ │ │ └── marker-shadow.png │ │ │ │ ├── text.js │ │ │ │ ├── factories │ │ │ │ │ └── controlPropertyFactory.js │ │ │ │ ├── profileConfigurator.js │ │ │ │ ├── spinner.js │ │ │ │ ├── message.js │ │ │ │ ├── interactivePlot.js │ │ │ │ ├── iframe.js │ │ │ │ ├── rPrint.js │ │ │ │ ├── htmlWidget.js │ │ │ │ ├── rText.js │ │ │ │ ├── actionButton.js │ │ │ │ ├── dataUpload.js │ │ │ │ ├── rPlot.js │ │ │ │ ├── dataDownload.js │ │ │ │ └── image.js │ │ │ ├── templates │ │ │ │ ├── pageMenuItem.tpl │ │ │ │ ├── selectOptions.tpl │ │ │ │ ├── dataSourceMenuItem.tpl │ │ │ │ ├── controlsMenu.tpl │ │ │ │ ├── timerMenuItem.tpl │ │ │ │ └── siteSettingsMenu.tpl │ │ │ ├── properties │ │ │ │ ├── templates │ │ │ │ │ ├── checkboxList.tpl │ │ │ │ │ ├── radioButtonGroup.tpl │ │ │ │ │ ├── multlineTextControl.tpl │ │ │ │ │ ├── ace.tpl │ │ │ │ │ ├── rangeControl.tpl │ │ │ │ │ ├── autocomplete.tpl │ │ │ │ │ ├── dropdownControl.tpl │ │ │ │ │ ├── textControl.tpl │ │ │ │ │ ├── wysiwyg.tpl │ │ │ │ │ ├── colorControl.tpl │ │ │ │ │ └── multiOptionControl.tpl │ │ │ │ ├── baseProperty.js │ │ │ │ ├── multilineTextProperty.js │ │ │ │ ├── radioButtonGroupProperty.js │ │ │ │ ├── autocompleteProperty.js │ │ │ │ ├── checkboxListProperty.js │ │ │ │ ├── wysiwygProperty.js │ │ │ │ ├── aceProperty.js │ │ │ │ ├── dropdownProperty.js │ │ │ │ ├── textProperty.js │ │ │ │ ├── colorProperty.js │ │ │ │ ├── rangeProperty.js │ │ │ │ └── stringValueProperty.js │ │ │ ├── message.js │ │ │ ├── infoBarManager.js │ │ │ ├── messageManager.js │ │ │ └── dialogUtils.js │ │ ├── profileManager.js │ │ ├── require-rcap.js │ │ ├── pages │ │ │ ├── pageManager.js │ │ │ └── page.js │ │ ├── utils │ │ │ ├── rCloud.js │ │ │ ├── rcapLogger.js │ │ │ ├── request.js │ │ │ ├── variableHandler.js │ │ │ ├── historyManager.js │ │ │ └── pageWalker.js │ │ ├── data │ │ │ ├── dataSource.js │ │ │ └── timer.js │ │ ├── versionConverters │ │ │ └── versionConverter.js │ │ ├── site │ │ │ └── siteSettings.js │ │ └── Class.js │ └── fonts │ │ ├── clvatt-book-webfont.eot │ │ ├── clvatt-book-webfont.ttf │ │ ├── clvatt-book-webfont.woff │ │ └── clvatt-book-webfont.woff2 ├── package.json ├── .jshintrc ├── bower.json └── Gruntfile.js ├── LICENSE ├── youtube.png ├── tests ├── testthat.R └── testthat │ ├── test-bfs.R │ ├── test-parse-controls.R │ ├── test-generate-table-css.R │ ├── test-controller.R │ ├── test-generate-table-ids-df.R │ ├── test-controls.R │ ├── testConfig.json │ └── test-topo-sort.R ├── .gitignore ├── .Rbuildignore ├── R ├── styles.R ├── ocaps.R ├── eventHandlerR6.R ├── showUpdates.R ├── rcap.user.profile.R ├── rcap.messageWidget.R ├── rcap.progressSpinner.R └── utils.R ├── app.Rproj ├── man ├── rcapSessionInfo.Rd ├── isEditMode.Rd ├── control_classes.Rd ├── RPlotControl.Rd ├── or.Rd ├── rcloudEnv.Rd ├── parseSizesJson.Rd ├── twistAdjlist.Rd ├── bfs.Rd ├── rcloud.rcap.caps.Rd ├── rcloud.rcap.global.functions.Rd ├── rcap.result.Rd ├── topologicalSort.Rd ├── Controller.Rd ├── getControls.Rd └── Control.Rd ├── NAMESPACE ├── .github └── ISSUE_TEMPLATE.md ├── DESCRIPTION └── README.md /inst/www/styles/default/default.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2012-2017 2 | COPYRIGHT HOLDER: AT&T Intellectual Property 3 | -------------------------------------------------------------------------------- /youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/youtube.png -------------------------------------------------------------------------------- /inst/www/partials/iframe.htm: -------------------------------------------------------------------------------- 1 |

The source of this iframe will be determined at runtime.

-------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(rcloud.rcap) 3 | 4 | test_check("rcloud.rcap") 5 | -------------------------------------------------------------------------------- /inst/www/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/img/spinner.gif -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/interactivePlot.tpl: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/submitButton.tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inst/www/fonts/clvatt-book-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/fonts/clvatt-book-webfont.eot -------------------------------------------------------------------------------- /inst/www/fonts/clvatt-book-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/fonts/clvatt-book-webfont.ttf -------------------------------------------------------------------------------- /inst/www/fonts/clvatt-book-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/fonts/clvatt-book-webfont.woff -------------------------------------------------------------------------------- /inst/www/fonts/clvatt-book-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/fonts/clvatt-book-webfont.woff2 -------------------------------------------------------------------------------- /inst/www/js/ui/controls/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/js/ui/controls/images/layers.png -------------------------------------------------------------------------------- /inst/www/js/ui/controls/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/js/ui/controls/images/layers-2x.png -------------------------------------------------------------------------------- /inst/www/js/ui/controls/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/js/ui/controls/images/marker-icon.png -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/htmlWidget.tpl: -------------------------------------------------------------------------------- 1 |
Please wait while the widget is loaded...
2 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/js/ui/controls/images/marker-icon-2x.png -------------------------------------------------------------------------------- /inst/www/js/ui/controls/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.rcap/develop/inst/www/js/ui/controls/images/marker-shadow.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | inst/www/vendor 2 | node_modules 3 | .sass-cache 4 | bower_components 5 | .Rproj.user 6 | attrcap.Rproj 7 | .vagrant 8 | /rcloud.rcap-develop 9 | output 10 | .vscode -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/profileConfigurator.tpl: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/actionButton.tpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/image.tpl: -------------------------------------------------------------------------------- 1 |
2 |
-------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/rText.tpl: -------------------------------------------------------------------------------- 1 |
Please wait while the content is loaded...
2 | <%if(designTimeDescription.length && isDesignTime) {%> 3 |
<%=designTimeDescription%>
4 | <%}%> -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/rPrint.tpl: -------------------------------------------------------------------------------- 1 |
Please wait while the content is loaded...
2 | <%if(designTimeDescription.length && isDesignTime) {%> 3 |
<%=designTimeDescription%>
4 | <%}%> -------------------------------------------------------------------------------- /inst/www/js/profileManager.js: -------------------------------------------------------------------------------- 1 | define(['pubsub', 'site/pubSubTable' 2 | ], function (/*PubSub, pubSubTable*/) { 3 | 4 | 'use strict'; 5 | 6 | var ProfileManager = function () { 7 | this.initialise = function () { 8 | 9 | }; 10 | }; 11 | 12 | return ProfileManager; 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^app.Rproj$ 2 | ^.github$ 3 | ^inst/bower_components$ 4 | ^inst/bower.json$ 5 | ^inst/build$ 6 | ^inst/Gruntfile.js$ 7 | ^inst/.jshintrc 8 | ^inst/internals.md 9 | ^inst/node_modules$ 10 | ^inst/package.json$ 11 | ^mkdist$ 12 | ^inst/npm-debug.log.* 13 | 14 | ^.*\.Rproj$ 15 | ^\.Rproj\.user$ 16 | -------------------------------------------------------------------------------- /inst/www/js/require-rcap.js: -------------------------------------------------------------------------------- 1 | requirejs.config(requirejs_config_obj); // jshint ignore:line 2 | 3 | var deps = common_deps; // jshint ignore:line 4 | 5 | deps.push( 6 | // rcloud's mini.js and bundle 7 | '../../shared.R/rcloud.rcap/js/rcap', 'rcloud_bundle'); 8 | 9 | start_require(deps); // jshint ignore:line 10 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/dataDownload.tpl: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /inst/www/partials/_404.htm: -------------------------------------------------------------------------------- 1 |

404 - Page not found

2 |

We are sorry, the page you requested cannot be found

3 |

This site contains the following (top level) pages:

4 | 9 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/iframe.tpl: -------------------------------------------------------------------------------- 1 | <% if(control.controlProperties[0].value.search('https?://')>-1) { %> 2 | 3 | <% } else { %> 4 | 5 | <% } %> 6 | 7 | 8 | -------------------------------------------------------------------------------- /inst/www/styles/loading.css: -------------------------------------------------------------------------------- 1 | 2 | #rcloud-rcap-loading { 3 | position: fixed; 4 | top: 50%; 5 | left: 50%; 6 | -webkit-transform: translate(-50%, -50%); 7 | -moz-transform: translate(-50%, -50%); 8 | -ms-transform: translate(-50%, -50%); 9 | -o-transform: translate(-50%, -50%); 10 | transform: translate(-50%, -50%); 11 | } 12 | -------------------------------------------------------------------------------- /inst/www/js/pages/pageManager.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/Class'], function() { 2 | 3 | 'use strict'; 4 | 5 | var PageManager = Class.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | 9 | 10 | 11 | } 12 | }); 13 | 14 | return PageManager; 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /inst/www/js/ui/templates/pageMenuItem.tpl: -------------------------------------------------------------------------------- 1 |
2 | Settings 3 | <% if(p.canAddChild) { %> 4 | Add child page 5 | <% } %> 6 | Duplicate 7 |
8 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/templates/newProfileVariable.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /R/styles.R: -------------------------------------------------------------------------------- 1 | 2 | getRCAPStyles <- function() { 3 | pkgs <- rownames(installed.packages()) 4 | style <- grep("^rcloud\\.rcap\\.style\\.", pkgs, value = TRUE) 5 | title <- vapply(style, function(x) packageDescription(x)$Title, "") 6 | desc <- vapply(style, function(x) packageDescription(x)$Description, "") 7 | unname(mapply(c, style, title, desc, SIMPLIFY = FALSE)) 8 | } 9 | -------------------------------------------------------------------------------- /app.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/dataUpload.tpl: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_confirmDialog.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 |

5 |
6 | 10 |
-------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/checkboxList.tpl: -------------------------------------------------------------------------------- 1 |
2 | 3 | <% _.each(property.checkboxListOptions, function(o, i){ %> 4 | 5 | 6 | 7 | 8 | <% }); %> 9 | 10 |
-------------------------------------------------------------------------------- /man/rcapSessionInfo.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{rcapSessionInfo} 4 | \alias{rcapSessionInfo} 5 | \title{Return relevant session info in a named list} 6 | \usage{ 7 | rcapSessionInfo() 8 | } 9 | \value{ 10 | A list with username and nodename (host) 11 | } 12 | \description{ 13 | This information is returned to the front end 14 | } 15 | 16 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/heading.tpl: -------------------------------------------------------------------------------- 1 | <<%=control.controlProperties[1].value%>> 2 | <% 3 | if(typeof control.controlProperties[0].value !== 'undefined' && control.controlProperties[0].value.length > 0) { 4 | %> 5 | <%=control.controlProperties[0].value%> 6 | <% }else {%> 7 | 8 | <%=control.controlProperties[0].defaultValue%> 9 | <% } %> 10 | 11 | > -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/message.tpl: -------------------------------------------------------------------------------- 1 |
2 | <% if(isDesignTime) { %> 3 | <% if(control.getPropertyValue('variablename')) { %><- <%=control.getPropertyValue('variablename')%><% } %> 4 | <% } else { %> 5 | 6 | <% } %> 7 |
8 | -------------------------------------------------------------------------------- /man/isEditMode.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rcap.result.R 3 | \name{isEditMode} 4 | \alias{isEditMode} 5 | \title{Is the page that called this function edit or mini (or derviatives)} 6 | \usage{ 7 | isEditMode() 8 | } 9 | \value{ 10 | Logical: TRUE if it is the edit page 11 | } 12 | \description{ 13 | Retrieves the url from the .session info and regex's it for edit.html 14 | } 15 | 16 | -------------------------------------------------------------------------------- /inst/www/js/ui/templates/selectOptions.tpl: -------------------------------------------------------------------------------- 1 |
2 | × 3 |
4 | <% if(label) { %> 5 |

Select a value for <%=label%>

6 | <% } %> 7 | <% _.each(values, function(o, i){ %> 8 | "><%=o.value%> 9 | <% }); %> 10 |
11 |
12 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/radioButtonGroup.tpl: -------------------------------------------------------------------------------- 1 |
2 | <% _.each(property.radioButtonOptions, function(o, i){ %> 3 | 4 | >
5 | <% }); %> 6 |
7 | 8 | -------------------------------------------------------------------------------- /inst/www/js/utils/rCloud.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | 'use strict'; 4 | 5 | return { 6 | getLoggedInUser: function() { 7 | return window.rcloud.username(); 8 | }, 9 | getRcapViewUrl: function() { 10 | return window.ui_utils.make_url('shared.R/rcloud.rcap/rcap.html', { // jshint ignore:line 11 | notebook: window.shell.gistname() 12 | }); 13 | } 14 | }; 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /man/control_classes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/controlTypes.R 3 | \docType{data} 4 | \name{control_classes} 5 | \alias{control_classes} 6 | \title{Front-end control types and matching back-end classes} 7 | \format{An object of class \code{list} of length 21.} 8 | \usage{ 9 | control_classes 10 | } 11 | \description{ 12 | Front-end control types and matching back-end classes 13 | } 14 | \keyword{datasets} 15 | 16 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/rPlot.tpl: -------------------------------------------------------------------------------- 1 | <%if(typeof control.controlProperties[1].value != 'undefined' && control.controlProperties[1].value.length) {%> 2 | 3 |
Please wait while the plot is loaded...
4 |
5 | <%} else {%> 6 |
Please wait while the plot is loaded...
7 | <% } %> -------------------------------------------------------------------------------- /inst/www/partials/viewer.htm: -------------------------------------------------------------------------------- 1 |
2 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /man/RPlotControl.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/controlTypes.R 3 | \docType{data} 4 | \name{RPlotControl} 5 | \alias{RPlotControl} 6 | \title{Control class for an R plot} 7 | \format{An object of class \code{R6ClassGenerator} of length 24.} 8 | \usage{ 9 | RPlotControl 10 | } 11 | \description{ 12 | It runs an R function to update the plot, and then pushes 13 | the new plot to the front-end. 14 | } 15 | \keyword{datasets} 16 | 17 | -------------------------------------------------------------------------------- /inst/www/js/utils/rcapLogger.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | 'use strict'; 4 | 5 | return function() { 6 | 7 | var me = this, 8 | messagesOn = localStorage.getItem('rcap-logging') === 'on'; 9 | 10 | ['log', 'info', 'warn', 'error'].forEach(function(consoleFunc) { 11 | me[consoleFunc] = messagesOn ? function() { console[consoleFunc].apply(console, arguments); } : function() {}; 12 | }); 13 | 14 | }; 15 | 16 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/separator.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl'], function(BaseControl) { 2 | 3 | 'use strict'; 4 | 5 | var SeparatorControl = BaseControl.extend({ 6 | init: function() { 7 | this._super({ 8 | type : 'separator', 9 | label : 'Separator', 10 | icon: 'ellipsis-horizontal', 11 | controlProperties: [ 12 | 13 | ] 14 | }); 15 | }, 16 | render: function() { 17 | return '
'; 18 | } 19 | }); 20 | 21 | return SeparatorControl; 22 | 23 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/spinner.tpl: -------------------------------------------------------------------------------- 1 |
2 | <% if(isDesignTime) { %> 3 | <% if(control.getPropertyValue('variablename')) { %><- <%=control.getPropertyValue('variablename')%><% } %> 4 | <% } else { %> 5 | 6 | 7 | <% } %> 8 |
9 | -------------------------------------------------------------------------------- /man/or.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{or} 4 | \alias{\%||\%} 5 | \alias{or} 6 | \title{Evaluate rhs if lhs is NULL} 7 | \usage{ 8 | l \%||\% r 9 | } 10 | \arguments{ 11 | \item{l}{Left hand side, evaluate this first.} 12 | 13 | \item{r}{Right had side, only evaluate it if \code{l} is \code{NULL}.} 14 | } 15 | \value{ 16 | \code{l} if it is not \code{NULL}, otherwise \code{r}. 17 | } 18 | \description{ 19 | Evaluate rhs if lhs is NULL 20 | } 21 | 22 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/multlineTextControl.tpl: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
<%=property.helpText%>
5 |
-------------------------------------------------------------------------------- /inst/www/partials/dialogs/_styleEditorDialog.htm: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Style Editor

4 |
5 |
6 |
7 |
8 | 12 |
-------------------------------------------------------------------------------- /inst/www/js/ui/templates/dataSourceMenuItem.tpl: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | 4 | <%if(ds.variable || ds.function) {%> 5 | Function: <%=ds.code%> 6 |
    7 | Variable: <%=ds.variable%> 8 | <%} else {%> 9 | This data source is not configured 10 | <%}%> 11 | Settings 12 |
    13 |
  • -------------------------------------------------------------------------------- /man/rcloudEnv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{rcloudEnv} 4 | \alias{rcloudEnv} 5 | \title{The environment the notebook and mini.html are run in} 6 | \usage{ 7 | rcloudEnv() 8 | } 9 | \value{ 10 | An environment. 11 | } 12 | \description{ 13 | This environment is used to search for functions that are used 14 | to update the dashboard, and for variables that are associated 15 | with dashboard controls. 16 | } 17 | \details{ 18 | This is currently the global environment. 19 | } 20 | 21 | -------------------------------------------------------------------------------- /inst/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RCloud.RCap", 3 | "version": "0.1.0", 4 | "devDependencies": { 5 | "bower": "~1.7.9", 6 | "grunt": "~0.4.2", 7 | "grunt-cli": "~1.1.0", 8 | "grunt-contrib-jshint": "^0.11.2", 9 | "grunt-contrib-watch": "^0.6.1", 10 | "grunt-newer": "^1.1.1", 11 | "grunt-nodemailer": "^0.3.0", 12 | "grunt-open": "^0.2.3", 13 | "grunt-sass": "^1.2.1", 14 | "jshint-stylish": "^2.0.1", 15 | "main-bower-files": "^2.13.1", 16 | "time-grunt": "^1.2.1", 17 | "uglifyjs": "2.4.10" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /inst/www/js/ui/templates/controlsMenu.tpl: -------------------------------------------------------------------------------- 1 |
    2 | <% _.each(controlCategories, function(category){ %> 3 |
    <%=category.type%>
    4 | 14 | <% }); %> 15 |
    -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/textField.tpl: -------------------------------------------------------------------------------- 1 | <% 2 | if(typeof control.controlProperties[0].value !== 'undefined' && control.controlProperties[0].value.length > 0) { 3 | %> 4 | 7 | <% } %> 8 | 9 | 10 | -------------------------------------------------------------------------------- /man/parseSizesJson.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/controllerR6.R 3 | \name{parseSizesJson} 4 | \alias{parseSizesJson} 5 | \title{Get plot sizes from JSON} 6 | \usage{ 7 | parseSizesJson(json) 8 | } 9 | \arguments{ 10 | \item{json}{A JSON string from the client. Must have an element named plotSizes} 11 | } 12 | \value{ 13 | List with control IDs as names and named vector of widths and heights 14 | as list items. 15 | } 16 | \description{ 17 | Parse the JSON string sent from the client, find the plot IDs and sizes, and 18 | return as a neat R object. 19 | } 20 | 21 | -------------------------------------------------------------------------------- /inst/www/js/ui/templates/timerMenuItem.tpl: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | 4 | <%if(t.variable && t.interval) {%> 5 | <%if(t.variable) {%> 6 |
    7 | Variable: <%=t.variable%> 8 | <%}%> 9 |
    10 | Every: <%=t.interval%> seconds 11 | <%} else {%> 12 | This timer is not configured 13 | <%}%> 14 | Settings 15 |
    16 |
  • -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_siteSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Site settings

    4 |
    5 |
    6 | 9 |
    10 | 11 |
    12 |
    13 | 17 |
    -------------------------------------------------------------------------------- /man/twistAdjlist.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/topo-sort.R 3 | \name{twistAdjlist} 4 | \alias{twistAdjlist} 5 | \title{Convert an in-adjacency list to an out-adjacency list, or the other way} 6 | \usage{ 7 | twistAdjlist(adjlist) 8 | } 9 | \arguments{ 10 | \item{adjlist}{The adjacencly list of the graph. See 11 | \code{topologicalSort} for the expected format.} 12 | } 13 | \value{ 14 | Another adjacency list, corresponding to the graph 15 | with all edge directions reversed. 16 | } 17 | \description{ 18 | In other words, it reverses the direction of each edge in a graph. 19 | } 20 | 21 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_designerSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Designer settings

    4 |
    5 |
    6 | 9 |
    10 | 11 |
    12 |
    13 | 17 |
    -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_viewerDataUpload.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Upload data

    4 |
    5 |
    6 | 9 |
    10 |
    11 |
    12 | 16 |
    17 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_profileSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Profile settings

    4 |
    5 |
    6 | 9 |
    id="profile-form"> 10 | 11 |
    12 |
    13 | 17 |
    18 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_viewerProfileSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Profile settings

    4 |
    5 |
    6 | 9 |
    10 | 11 |
    12 |
    13 | 17 |
    18 | -------------------------------------------------------------------------------- /inst/www/js/data/dataSource.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/Class'], function() { 2 | 3 | 'use strict'; 4 | 5 | var DataSource = Class.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | 9 | this.id = 'rcap' + Math.random().toString(16).slice(2); 10 | this.variable = ''; 11 | this.code = ''; 12 | }, 13 | toJSON: function() { 14 | return { 15 | 'id' : this.id, 16 | 'variable': this.variable, 17 | 'code': this.code, 18 | 'type': 'dataSource' 19 | }; 20 | } 21 | }); 22 | 23 | return DataSource; 24 | }); -------------------------------------------------------------------------------- /man/bfs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/topo-sort.R 3 | \name{bfs} 4 | \alias{bfs} 5 | \title{BFS of a graph.} 6 | \usage{ 7 | bfs(adjlist, seeds) 8 | } 9 | \arguments{ 10 | \item{adjlist}{The adjacency list of the graph. See 11 | \code{topologicalSort} for the expected format.} 12 | 13 | \item{seeds}{Character vector, vertex ids to start the BFS from.} 14 | } 15 | \value{ 16 | Character vector with all vertex ids that are reachable 17 | from the \code{seeds}. 18 | } 19 | \description{ 20 | From the given vertices we collect all vertices that the reachable 21 | from them. There are all the controls that we need to update. 22 | } 23 | 24 | -------------------------------------------------------------------------------- /inst/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "globals": { 21 | "angular": false, 22 | "require": false, 23 | "define" : false, 24 | "_" : false, 25 | "$" : false, 26 | "RCloud" : false, 27 | "Notebook" : false, 28 | "alert" : false, 29 | "Class" : false, 30 | "requirejs": false 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_controlSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Control settings

    4 |
    5 |
    6 | 9 |
    10 | 11 |
    12 |
    13 | 18 |
    -------------------------------------------------------------------------------- /inst/www/partials/dialogs/templates/viewerDataDownload.tpl: -------------------------------------------------------------------------------- 1 | <% if(files.length) { %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <% _.each(files, function(file){ %> 12 | 13 | 14 | 15 | 16 | 17 | <% }); %> 18 | 19 |
    NameSizeLast modified
    <%=file.filename%><%=file.filesize%><%=file.lastmodified%>
    20 | <% } else { %> 21 |

    No files available

    22 | <% } %> -------------------------------------------------------------------------------- /inst/www/partials/dialogs/templates/profileVariables.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <% _.each(profileVariables, function(o, i){ %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% }); %> 19 | 20 |
    Variable nameFunctionDescription
    21 | 22 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/ace.tpl: -------------------------------------------------------------------------------- 1 | <%if(property.isHorizontal) { %> 2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 9 |
    <%=property.helpText%>
    10 |
    11 |
    12 | 13 | <% } else { %> 14 | 15 |
    16 | 17 | 18 | 19 | 20 |
    <%=property.helpText%>
    21 | 22 |
    23 | 24 | <% } %> -------------------------------------------------------------------------------- /inst/www/js/ui/templates/siteSettingsMenu.tpl: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |
    6 | 7 |
    8 |
    9 | 15 |
    The amount of padding around each control (in pixels)
    16 |
    17 |
    18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
    -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/datePicker.tpl: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | <% 16 | if(typeof control.controlProperties[0].value !== 'undefined' && control.controlProperties[0].value.length > 0) { 17 | %> 18 | 21 | <% } %> 22 | -------------------------------------------------------------------------------- /inst/www/styles/parsleyjs/parsley.scss: -------------------------------------------------------------------------------- 1 | input.parsley-success, 2 | select.parsley-success, 3 | textarea.parsley-success { 4 | color: #468847; 5 | background-color: #DFF0D8; 6 | border: 1px solid #D6E9C6; 7 | } 8 | 9 | input.parsley-error, 10 | select.parsley-error, 11 | textarea.parsley-error { 12 | color: #B94A48; 13 | background-color: #F2DEDE; 14 | border: 1px solid #EED3D7; 15 | } 16 | 17 | .parsley-errors-list { 18 | margin: 2px 0 3px; 19 | padding: 0; 20 | list-style-type: none; 21 | font-size: 0.9em; 22 | line-height: 0.9em; 23 | opacity: 0; 24 | 25 | transition: all .3s ease-in; 26 | -o-transition: all .3s ease-in; 27 | -moz-transition: all .3s ease-in; 28 | -webkit-transition: all .3s ease-in; 29 | } 30 | 31 | .parsley-errors-list.filled { 32 | opacity: 1; 33 | } 34 | -------------------------------------------------------------------------------- /man/rcloud.rcap.caps.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/zzz.R 3 | \docType{data} 4 | \name{rcloud.rcap.caps} 5 | \alias{rcloud.rcap.caps} 6 | \title{List of OCAPS} 7 | \format{An object of class \code{NULL} of length 0.} 8 | \usage{ 9 | rcloud.rcap.caps 10 | } 11 | \description{ 12 | \itemize{ 13 | \item \code{getRFunctions} Query all R functions in the 14 | rcloud environment. It calls 15 | \code{\link{rcloud.rcap.global.functions}}. 16 | \item \code{getRTime} Query the current time of the R interpreter. 17 | This is used for debugging. 18 | \item \code{updateControls} Send an update request from the 19 | front-end to the back-end. It calls the \code{update} method 20 | of the Controller. 21 | } 22 | } 23 | \keyword{datasets} 24 | 25 | -------------------------------------------------------------------------------- /man/rcloud.rcap.global.functions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ocaps.R 3 | \name{rcloud.rcap.global.functions} 4 | \alias{rcloud.rcap.global.functions} 5 | \title{Find all functions in the rcloud environment} 6 | \usage{ 7 | rcloud.rcap.global.functions() 8 | } 9 | \value{ 10 | A character vector of function names 11 | } 12 | \description{ 13 | The rcloud environment is the environment the notebook 14 | runs in. This is currently the global environment, i.e. .GlobalEnv. 15 | } 16 | \details{ 17 | This function is used by the designer, to provide suggestions 18 | whenever the designer user needs to supply an R function name; 19 | E.g. when creating an R plot, the function that does the plotting, 20 | or when a dropdown is populated by an R function. 21 | } 22 | 23 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_viewerDataDownload.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Download data

    4 |
    5 |
    6 |
    7 | 8 |
    9 |
    10 |
    11 | Downloading 12 |
    13 | 16 |
    17 |
    18 | 21 |
    -------------------------------------------------------------------------------- /R/ocaps.R: -------------------------------------------------------------------------------- 1 | 2 | #' Find all functions in the rcloud environment 3 | #' 4 | #' The rcloud environment is the environment the notebook 5 | #' runs in. This is currently the global environment, i.e. .GlobalEnv. 6 | #' 7 | #' This function is used by the designer, to provide suggestions 8 | #' whenever the designer user needs to supply an R function name; 9 | #' E.g. when creating an R plot, the function that does the plotting, 10 | #' or when a dropdown is populated by an R function. 11 | #' 12 | #' @return A character vector of function names 13 | 14 | rcloud.rcap.global.functions <- function() { 15 | 16 | ## Get all objects 17 | globalObjects <- ls(envir = rcloudEnv()) 18 | 19 | ## Keep the functions 20 | Filter( 21 | function(x) is.function(get(x, envir = rcloudEnv())), 22 | globalObjects 23 | ) 24 | } 25 | 26 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/checkboxList.tpl: -------------------------------------------------------------------------------- 1 |

    <%=control.controlProperties[0].value%>

    2 |
    3 | <% if( control.controlProperties[2].optionType == 'manual') { %> 4 | <% _.each(control.controlProperties[2].value, function(o, i){ %> 5 |
    6 | 7 |
    8 | <% }); %> 9 | <% } else { %> 10 | <% if(isDesignTime) { %> 11 | 12 | <% } else { %> 13 | 14 | <% } %> 15 | <% } %> 16 |
    17 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/leaflet.tpl: -------------------------------------------------------------------------------- 1 | <%if(typeof control.controlProperties[1].value !== 'undefined' && control.controlProperties[1].value.length) {%> 2 |
    Please wait while the map is loaded...
    3 |
    4 | <%} else {%> 5 |
    Please wait while the map is loaded...
    6 | <% } %> 7 | 8 | 9 | <%if(typeof control.controlProperties[1].value !== 'undefined' && control.controlProperties[1].value.length) {%> 10 | 15 | <% } %> -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/radioButtonGroup.tpl: -------------------------------------------------------------------------------- 1 |

    <%=control.controlProperties[0].value%>

    2 |
    3 | <% if( control.controlProperties[2].optionType == 'manual') { %> 4 | <% _.each(control.controlProperties[2].value, function(o, i){ %> 5 |
    6 | 7 |
    8 | <% }); %> 9 | <% } else { %> 10 | <% if(isDesignTime) { %> 11 | 12 | <% } else { %> 13 | 14 | <% } %> 15 | <% } %> 16 |
    17 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/text.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/gridControl', 2 | 'rcap/js/ui/properties/textProperty', 3 | 'rcap/js/ui/properties/wysiwygProperty'], function(GridControl, TextProperty, WysiwygProperty) { 4 | 5 | 'use strict'; 6 | 7 | var TextControl = GridControl.extend({ 8 | init: function() { 9 | this._super({ 10 | type : 'text', 11 | controlCategory: 'HTML', 12 | label : 'Text', 13 | icon: 'pencil', 14 | controlProperties: [ 15 | new WysiwygProperty({ 16 | uid: 'content', 17 | label : 'Content', 18 | defaultValue : '', 19 | isRequired: true 20 | }) 21 | ] 22 | }); 23 | }, 24 | render: function() { 25 | return '
    ' + this.controlProperties[0].value + '
    '; 26 | } 27 | }); 28 | 29 | return TextControl; 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /man/rcap.result.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rcap.result.R 3 | \name{rcap.result} 4 | \alias{rcap.result} 5 | \title{Set up a dashboard} 6 | \usage{ 7 | rcap.result(rcapConfigFileName = "rcap_designer.json") 8 | } 9 | \arguments{ 10 | \item{rcapConfigFileName}{Character vector: 11 | The name of the json file in the assets.} 12 | } 13 | \description{ 14 | This function must called from the last cell of the notebook. 15 | When running in mini.html, it requests the asset that contains the 16 | configuration of the dashboard, and initializes the viewer from it. 17 | } 18 | \details{ 19 | Then it creates the server side controller, and the controller 20 | creates all the control objects that corresponds to the elements 21 | of the dashboard. 22 | 23 | This function should not be called from the notebook itself. 24 | } 25 | 26 | -------------------------------------------------------------------------------- /inst/www/js/ui/message.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | 'use strict'; 4 | 5 | var Message = function(options) { 6 | 7 | this.validMessageTypes = ['Information', 'Warning', 'Error']; 8 | 9 | options = options || {}; 10 | 11 | var messageType = options.messageType || 'Information'; 12 | 13 | if(this.validMessageTypes.indexOf(messageType) === -1) { 14 | throw new Error('Message type ' + messageType + ' is not valid.'); 15 | } 16 | 17 | if(!options.content){ 18 | throw new Error('Expected non zero length content parameter'); 19 | } 20 | 21 | this.messageType = messageType; 22 | this.content = options.content; 23 | 24 | }; 25 | 26 | Message.prototype.getValidMessageTypes = function() { 27 | return this.validMessageTypes; 28 | }; 29 | 30 | return Message; 31 | }); 32 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/rangeControl.tpl: -------------------------------------------------------------------------------- 1 | <% if(property.isHorizontal) { %> 2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 9 |
    10 |
    11 | 12 | <% } else { %> 13 | 14 |
    15 | 16 | 17 |
    18 | 19 | <% } %> 20 | 21 | 22 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/dropdown.tpl: -------------------------------------------------------------------------------- 1 | <% 2 | if(typeof control.controlProperties[0].value !== 'undefined' && control.controlProperties[0].value.length > 0) { 3 | %> 4 | 7 | <% } %> 8 | 9 | 28 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/slider.tpl: -------------------------------------------------------------------------------- 1 | <% 2 | if(typeof control.controlProperties[0].value !== 'undefined' && control.controlProperties[0].value.length > 0) { 3 | %> 4 | 7 | <% } %> 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/testthat/test-bfs.R: -------------------------------------------------------------------------------- 1 | 2 | context("BFS") 3 | 4 | test_that("BFS on a dependency graph", { 5 | 6 | G <- list( 7 | "a" = "b", 8 | "b" = character(), 9 | "c" = "d", 10 | "d" = character(), 11 | "e" = "f", 12 | "f" = character() 13 | ) 14 | 15 | expect_equal(bfs(G, "a"), "b") 16 | expect_equal(bfs(G, "c"), "d") 17 | expect_equal(sort(bfs(G, c("a", "c"))), c("b", "d")) 18 | 19 | G <- list( 20 | "7" = c("11", "8"), 21 | "5" = "11", 22 | "3" = c("8", "10"), 23 | "11" = c("2", "9", "10"), 24 | "8" = "9", 25 | "2" = character(), 26 | "9" = character(), 27 | "10" = character() 28 | ) 29 | 30 | expect_equal(sort(bfs(G, character())), character()) 31 | expect_equal(sort(bfs(G, "10")), character()) 32 | expect_equal(sort(bfs(G, "7")), c("10", "11", "2", "8", "9")) 33 | expect_equal( 34 | sort(bfs(G, c("7", "5"))), 35 | c("10", "11", "2", "8", "9") 36 | ) 37 | }) 38 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(rcap.result) 4 | export(rcapSessionInfo) 5 | export(showUpdates) 6 | export(rcap.getDataUploadPath) 7 | export(rcap.ls.datasets) 8 | export(rcap.ls.dataset.files) 9 | export(rcap.messageWidget.msg) 10 | export(rcap.messageWidget.write) 11 | export(rcap.progressSpinner.msg) 12 | export(rcap.progressSpinner.write) 13 | importFrom(R6,R6Class) 14 | importFrom(Rserve,Rserve.context) 15 | importFrom(codetools,findGlobals) 16 | importFrom(jsonlite,fromJSON) 17 | import(rcloud.htmlwidgets) 18 | importFrom(rcloud.support,RCloudDevice) 19 | importFrom(rcloud.support,rcloud.flush.plot) 20 | importFrom(rcloud.support,rcloud.get.asset) 21 | importFrom(rcloud.support,rcloud.get.url) 22 | importFrom(rcloud.support,rcloud.html.out) 23 | importFrom(rcloud.support,rcloud.output.context) 24 | importFrom(rcloud.web,rcw.append) 25 | importFrom(rcloud.web,rcw.resolve) 26 | importFrom(rcloud.web,rcw.result) 27 | importFrom(rcloud.web,rcw.set) 28 | -------------------------------------------------------------------------------- /tests/testthat/test-parse-controls.R: -------------------------------------------------------------------------------- 1 | 2 | context("Parsing controls") 3 | 4 | test_that("we can parse controls from JSON", { 5 | resp <- paste(readLines("testConfig.json"), collapse = "\n") 6 | resp <- fromJSON(resp, simplifyVector = FALSE) 7 | ctrls <- getControls(resp) 8 | 9 | expect_equal(length(ctrls), 11) 10 | expect_equal( 11 | vapply(ctrls, "[[", "", "type"), 12 | c("datepicker", "datepicker", "separator", "checkboxlist", "form", 13 | "rplot", "image", "rplot", "rplot", "datepicker", "form") 14 | ) 15 | expect_equal(names(ctrls[[1]]), c("type", "id", "controlProperties")) 16 | expect_equal( 17 | names(ctrls[[5]]), 18 | c("type", "x", "y", "width", "height", "id", "controlProperties", 19 | "childControls") 20 | ) 21 | expect_equal( 22 | names(ctrls[[6]]), 23 | c("type", "x", "y", "width", "height", "id", "controlProperties", 24 | "isOnGrid") 25 | ) 26 | expect_equal(length(ctrls[[1]]$controlProperties), 2) 27 | }) 28 | -------------------------------------------------------------------------------- /inst/www/js/ui/infoBarManager.js: -------------------------------------------------------------------------------- 1 | define(['pubsub', 'site/pubSubTable'], function(PubSub, pubSubTable) { 2 | 3 | 'use strict'; 4 | 5 | var InfoBarManager = function() { 6 | 7 | this.initialise = function() { 8 | 9 | PubSub.subscribe(pubSubTable.changeSelectedPage, function(msg, page) { 10 | $('#current-page') 11 | .data('id', page.id) 12 | .html(page.navigationTitle); 13 | }); 14 | 15 | PubSub.subscribe(pubSubTable.updatePage, function(msg, page) { 16 | if($('#current-page').data('id') === page.id) { 17 | $('#current-page').html(page.navigationTitle); 18 | } 19 | }); 20 | 21 | // 22 | $('#current-page-status').click(function() { 23 | PubSub.publish(pubSubTable.showPageFlyout); 24 | }); 25 | 26 | this.initialise = function() {}; 27 | }; 28 | }; 29 | 30 | return InfoBarManager; 31 | }); 32 | -------------------------------------------------------------------------------- /R/eventHandlerR6.R: -------------------------------------------------------------------------------- 1 | #' Abstract class for event handlers 2 | #' 3 | #' @section Methods: 4 | #' 5 | #' \code{supports(event = list())} \code{TRUE} if handler supports the event, \code{FALSE} otherwise 6 | #' 7 | #' \code{handle(event = list())} handles the event, returns a response object with outcome of the processing and result data. 8 | #' 9 | EventHandler <- R6::R6Class("EventHandler", 10 | 11 | public = list( 12 | 13 | initialize = function() { 14 | 15 | }, 16 | 17 | supports = function(event = list()) { 18 | FALSE 19 | }, 20 | handle = function(event = list()) { 21 | 22 | } 23 | ), 24 | 25 | private = list( 26 | ) 27 | ) -------------------------------------------------------------------------------- /tests/testthat/test-generate-table-css.R: -------------------------------------------------------------------------------- 1 | 2 | context("Generate Table CSS") 3 | 4 | test_that("createTableCss returns the expected results", { 5 | 6 | colNames <- colnames(mtcars) 7 | 8 | options <- list(columnWidths = c("50%", rep("5%", length(colnames(mtcars)) - 1)), 9 | rightAlign = "hp", 10 | columnColor = c("mpg" = "red", "cyl" = "blue", "drat" = "green"), 11 | textColor = c("mpg" = "green", "drat" = "yellow", "hp" = "red")) 12 | 13 | getClassNames <- createTableIdDf(options = options, colNames = colNames, id = "table") 14 | 15 | getCss <- createTableCss(getClassNames, id = "table") 16 | 17 | expectedRes <- "" 18 | 19 | expect_equal(getCss, expectedRes) 20 | }) -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/autocomplete.tpl: -------------------------------------------------------------------------------- 1 | <%if(property.isHorizontal) { %> 2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 9 |
    <%=property.helpText%>
    10 |
    11 |
    12 | 13 | <% } else { %> 14 | 15 |
    16 | 17 | 18 |
    <%=property.helpText%>
    19 |
    20 | 21 | <% } %> 22 | 23 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/templates/multiSelect.tpl: -------------------------------------------------------------------------------- 1 | <% 2 | if(typeof control.controlProperties[0].value !== 'undefined' && control.controlProperties[0].value.length > 0) { 3 | %> 4 | 7 | <% } %> 8 | 9 | 22 | 23 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_executionOrderDialog.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Execution Order

    4 |
    5 |
    6 | 9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
    PageControl typeFunctionExecution Order Hint
    21 |
    22 |
    23 | 28 |
    29 | -------------------------------------------------------------------------------- /inst/www/styles/viewercontrols.scss: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // RCAP Controls, View Mode 4 | // 5 | 6 | .select2-selection__choice { 7 | color: black; 8 | } 9 | 10 | .select2-container--default .select2-selection--multiple { 11 | border-radius: 0; 12 | } 13 | 14 | ////////////////////////////////////////////// page menu ///////////////////////////////////////////////// 15 | .rcap-pagemenu { 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | .rcap-pagemenu li { 21 | padding: 0; 22 | color: #369; 23 | list-style-type: none; 24 | } 25 | 26 | .rcap-pagemenu a { 27 | padding: 5px; 28 | display: block; 29 | text-decoration: none; 30 | } 31 | 32 | .rcap-pagemenu li.current { 33 | background-color: #369; 34 | } 35 | 36 | .rcap-pagemenu li.current a { 37 | color: white; 38 | } 39 | 40 | .rcap-pagemenu li:not(.current) a:hover { 41 | background-color: lighten(#369, 50%); 42 | } 43 | 44 | .rcap-pagemenu-horizontal li { 45 | float: left; 46 | } 47 | 48 | .rcap-pagemenu-hamburger {} 49 | -------------------------------------------------------------------------------- /tests/testthat/test-controller.R: -------------------------------------------------------------------------------- 1 | 2 | context("Controller") 3 | 4 | test_that("controller builds dependency graph and update order", { 5 | 6 | resp <- paste(readLines("testConfig.json"), collapse = "\n") 7 | resp <- fromJSON(resp, simplifyVector = FALSE) 8 | 9 | ## TODO: this is a hack that will break at some point 10 | on.exit(rm(list = "makePlot1", envir = .GlobalEnv), add = TRUE) 11 | assign( 12 | "makePlot1", 13 | function() { datePicker1 + datePicker2 }, 14 | envir = .GlobalEnv 15 | ) 16 | 17 | skip("Cannot test currently without rcloud") 18 | 19 | cnt <- Controller$new(resp) 20 | succList <- cnt$.__enclos_env__$private$succList 21 | 22 | expect_equal(succList$rcap16014ed1, rcap630974bf) 23 | expect_equal(succList$rcapc43838c4, rcap630974bf) 24 | 25 | topoSort <- cnt$.__enclos_env__$private$topoSort 26 | 27 | expect_equal(sort(names(succList)), sort(topoSort)) 28 | expect_true( 29 | match("rcapc43838c4", topoSort) < match(rcap630974bf, topoSort) 30 | ) 31 | expect_true( 32 | match("rcap16014ed1", topoSort) < match(rcap630974bf, topoSort) 33 | ) 34 | }) 35 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/submitButton.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 'rcap/js/ui/properties/textProperty', 2 | 'text!rcap/js/ui/controls/child/templates/submitButton.tpl'], function(BaseControl, TextProperty, tpl) { 3 | 4 | 'use strict'; 5 | 6 | var SubmitButtonControl = BaseControl.extend({ 7 | init: function() { 8 | this._super({ 9 | type: 'submitbutton', 10 | label: 'Submit Button', 11 | icon: 'youtube-play', 12 | controlProperties: [ 13 | new TextProperty({ 14 | uid: 'text', 15 | label: 'Text', 16 | defaultValue: 'Submit', 17 | isHorizontal: false 18 | }) 19 | ] 20 | }); 21 | }, 22 | render: function() { 23 | var template = _.template(tpl); 24 | 25 | return template({ 26 | text: this.getPropertyValueOrDefault('text') 27 | }); 28 | } 29 | }); 30 | 31 | return SubmitButtonControl; 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /tests/testthat/test-generate-table-ids-df.R: -------------------------------------------------------------------------------- 1 | 2 | context("Generate Table IDs DataFrame") 3 | 4 | test_that("createTableIdDf returns the expected results", { 5 | 6 | colNames <- colnames(mtcars) 7 | 8 | options <- list(columnWidths = c("50%", rep("5%", length(colnames(mtcars)) - 1)), 9 | rightAlign = "hp", 10 | columnColor = c("mpg" = "red", "cyl" = "blue", "drat" = "green"), 11 | textColor = c("mpg" = "green", "drat" = "yellow", "hp" = "red")) 12 | 13 | getClassNames <- createTableIdDf(options = options, colNames = colNames, id = "table") 14 | expectedRes <- structure( 15 | list( 16 | targets = c("cyl", "drat", "hp", "hp", "mpg"), 17 | className = c("dt-table-1", "dt-table-4", "dt-body-right dt-table-3", "dt-table-3", "dt-table-0"), 18 | `background-color` = c("blue", "green", NA, NA, "red"), 19 | color = c(NA, "yellow", NA, "red", "green")), 20 | .Names = c("targets", "className", "background-color", "color"), 21 | row.names = c(NA, 5L), 22 | class = "data.frame") 23 | 24 | expect_equal(getClassNames, expectedRes) 25 | }) -------------------------------------------------------------------------------- /inst/www/js/utils/request.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | 'use strict'; 4 | 5 | return { 6 | getUrlSearchValue : function(query) { 7 | if(_.isUndefined(window.URLSearchParams)) { 8 | return undefined; 9 | } 10 | var searchParams = new window.URLSearchParams(window.location.search); 11 | return searchParams.get(query); 12 | }, 13 | getGridPreferences : function() { 14 | var width = this.getUrlSearchValue('width'), 15 | height = this.getUrlSearchValue('height'), 16 | align = this.getUrlSearchValue('align'), 17 | prefs = {}; 18 | 19 | if(width && !isNaN(width)) { 20 | prefs.width = +width; 21 | } 22 | 23 | if(height && !isNaN(height)) { 24 | prefs.height = +height; 25 | } 26 | 27 | if(align && ['left', 'center'].indexOf(align) !== -1) { 28 | prefs.align = align; 29 | } 30 | 31 | return Object.keys(prefs).length ? prefs : null; 32 | } 33 | }; 34 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/controls/factories/controlPropertyFactory.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'rcap/js/ui/properties/colorProperty', 3 | 'rcap/js/ui/properties/dropdownProperty', 4 | 'rcap/js/ui/properties/textProperty', 5 | 'rcap/js/ui/properties/wysiwygProperty' 6 | ], function(ColorProperty, DropdownProperty, TextProperty, WysiwygProperty) { 7 | 8 | 'use strict'; 9 | 10 | function PropertyFactory() { 11 | this.controlProperties = [ 12 | new ColorProperty(), 13 | new DropdownProperty(), 14 | new TextProperty(), 15 | new WysiwygProperty() 16 | ]; 17 | } 18 | 19 | PropertyFactory.prototype.getAll = function() { 20 | return this.controlProperties; 21 | }; 22 | 23 | PropertyFactory.prototype.getByKey = function(key) { 24 | 25 | var Property; 26 | 27 | switch(key){ 28 | case 'color': Property = new ColorProperty(); break; 29 | case 'dropdown': Property = new DropdownProperty(); break; 30 | case 'text': Property = new TextProperty(); break; 31 | case 'wysiwyg': Property = new WysiwygProperty(); break; 32 | default: Property = undefined; break; 33 | } 34 | 35 | return Property; 36 | }; 37 | 38 | return PropertyFactory; 39 | 40 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/controls/profileConfigurator.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/gridControl', 2 | 'text!controlTemplates/profileConfigurator.tpl', 3 | 'pubsub', 4 | 'site/pubSubTable', 5 | ], function(GridControl, tpl, PubSub, pubSubTable) { 6 | 7 | 'use strict'; 8 | 9 | var ProfileConfiguratorControl = GridControl.extend({ 10 | init: function() { 11 | this._super({ 12 | type: 'profileconfigurator', 13 | controlCategory: 'Dynamic', 14 | label: 'Profile', 15 | icon: 'user', 16 | controlProperties: [] 17 | }); 18 | }, 19 | render: function() { 20 | 21 | var template = _.template(tpl); 22 | 23 | return template({ 24 | control: this 25 | }); 26 | 27 | }, 28 | initialiseViewerItems: function() { 29 | $('.grid-stack-item-content.rcap-controltype-profileconfigurator').click(function() { 30 | PubSub.publish(pubSubTable.getViewerProfile); 31 | }); 32 | } 33 | }); 34 | 35 | return ProfileConfiguratorControl; 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/baseProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/Class'], function() { 2 | 3 | 'use strict'; 4 | 5 | var BaseProperty = Class.extend({ 6 | init : function(options) { 7 | 8 | options = options || {}; 9 | 10 | this.uid = options.uid; 11 | this.type = options.type; 12 | this.label = options.label; 13 | this.helpText = options.helpText; 14 | this.defaultValue = options.defaultValue; 15 | this.value = options.value; 16 | this.isRequired = options.isRequired || false; 17 | this.className = options.className || ''; 18 | this.id = 'ctrl' + this.type + Math.random().toString(16).slice(2); 19 | 20 | }, 21 | toJSON : function() { 22 | return { 23 | 'uid': this.uid, 24 | 'value': this.value, 25 | 'id': this.id 26 | }; 27 | }, 28 | render : function() { 29 | return ''; 30 | }, 31 | getDialogValue : function() { 32 | return ''; 33 | }, 34 | getValueOrDefault : function() { 35 | if(_.isUndefined(this.value)) { 36 | return this.defaultValue; 37 | } 38 | 39 | return this.value; 40 | }, 41 | finalise: function() { 42 | 43 | } 44 | }); 45 | 46 | return BaseProperty; 47 | 48 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/properties/multilineTextProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/multlineTextControl.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var MultiLineTextProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type : 'multilinetext', 10 | label : options.label || '', 11 | helpText : options.helpText || '', 12 | defaultValue : options.defaultValue || '', 13 | isRequired : options.isRequired || false, 14 | uid : options.uid, 15 | className : options.className, 16 | value: options.value 17 | }); 18 | 19 | // additional assignments go here: 20 | this.rows = options.rows || 10; 21 | this.cols = options.cols || 80; 22 | }, 23 | render: function(childIndex) { 24 | 25 | var template = _.template(tpl); 26 | 27 | return template({ 28 | property : this, 29 | childIndex : childIndex 30 | }); 31 | 32 | }, 33 | getDialogValue : function() { 34 | return $('#' + this.id).val(); 35 | } 36 | }); 37 | 38 | return MultiLineTextProperty; 39 | 40 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/properties/radioButtonGroupProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/radioButtonGroup.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var RadioButtonGroupProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type : 'radiobuttongroup', 10 | label : options.label || '', 11 | helpText : options.helpText || '', 12 | defaultValue : options.defaultValue || '', 13 | isRequired : options.isRequired || false, 14 | uid : options.uid, 15 | className : options.className 16 | }); 17 | 18 | // additional assignments go here: 19 | this.radioButtonOptions = options.radioButtonOptions || []; 20 | }, 21 | render: function(childIndex) { 22 | 23 | var template = _.template(tpl); 24 | 25 | return template({ 26 | property : this, 27 | childIndex : childIndex 28 | }); 29 | 30 | }, 31 | getDialogValue : function() { 32 | return $('#' + this.id + ' input[type="radio"]:checked:first').val(); 33 | } 34 | }); 35 | 36 | return RadioButtonGroupProperty; 37 | 38 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/properties/autocompleteProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/autocomplete.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var AutocompleteProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type : 'autocomplete', 10 | label : options.label || '', 11 | helpText : options.helpText || '', 12 | defaultValue : options.defaultValue || '', 13 | isRequired : options.isRequired || false, 14 | uid : options.uid, 15 | className : options.className, 16 | value: options.value 17 | }); 18 | 19 | this.serviceName = options.serviceName || 'getRFunctions'; 20 | this.isHorizontal = _.isUndefined(options.isHorizontal) ? true : options.isHorizontal; 21 | }, 22 | render: function(childIndex) { 23 | 24 | var template = _.template(tpl); 25 | 26 | return template({ 27 | property : this, 28 | childIndex : childIndex 29 | }); 30 | 31 | }, 32 | getDialogValue : function() { 33 | return $('#' + this.id).val(); 34 | } 35 | }); 36 | 37 | return AutocompleteProperty; 38 | 39 | }); 40 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/spinner.js: -------------------------------------------------------------------------------- 1 | define(['pubsub', 2 | 'site/pubSubTable', 3 | 'rcap/js/ui/controls/gridControl', 4 | 'rcap/js/ui/properties/textProperty', 5 | 'text!controlTemplates/spinner.tpl' 6 | ], function (PubSub, pubSubTable, GridControl, TextProperty, tpl) { 7 | 8 | 'use strict'; 9 | 10 | var SpinnerControl = GridControl.extend({ 11 | init: function () { 12 | this._super({ 13 | type: 'spinner', 14 | controlCategory: 'Dynamic', 15 | label: 'Spinner', 16 | icon: 'spinner', 17 | initialSize: [2, 2], 18 | controlProperties: [ 19 | new TextProperty({ 20 | uid: 'variablename', 21 | label: 'Variable', 22 | helpText: 'The variable associated with this spinner', 23 | isRequired: true 24 | }) 25 | ] 26 | }); 27 | }, 28 | render: function (options) { 29 | options = options || {}; 30 | 31 | var template = _.template(tpl); 32 | 33 | return template({ 34 | control: this, 35 | isDesignTime: options.isDesignTime || false 36 | }); 37 | 38 | }, 39 | initialiseViewerItems: function () { 40 | 41 | } 42 | }); 43 | 44 | return SpinnerControl; 45 | 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/checkboxListProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/checkboxList.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var CheckboxListProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type : 'radiobuttongroup', 10 | label : options.label || '', 11 | helpText : options.helpText || '', 12 | defaultValue : options.defaultValue || '', 13 | isRequired : options.isRequired || false, 14 | uid : options.uid, 15 | className : options.className 16 | }); 17 | 18 | // additional assignments go here: 19 | this.checkboxListOptions = options.checkboxListOptions || []; 20 | }, 21 | render: function(childIndex) { 22 | 23 | var template = _.template(tpl); 24 | 25 | return template({ 26 | property : this, 27 | childIndex : childIndex 28 | }); 29 | 30 | }, 31 | getDialogValue : function() { 32 | 33 | return $('#' + this.id + ' input:checkbox:checked').map(function() { 34 | return $(this).val(); 35 | }).get(); 36 | 37 | } 38 | }); 39 | 40 | return CheckboxListProperty; 41 | 42 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/properties/wysiwygProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 2 | 'text!templates/wysiwyg.tpl', 3 | 'quill/quill', 4 | 'rcap/js/utils/quillRcapLink' 5 | ], function(BaseProperty, tpl, Quill, registerQuillLink) { // jshint ignore:line 6 | 'use strict'; 7 | 8 | var WysiwygProperty = BaseProperty.extend({ 9 | init: function(options) { 10 | options = options || {}; 11 | this._super({ 12 | type : 'wysiwyg', 13 | label : options.label || '', 14 | helpText : options.helpText || '', 15 | defaultValue : options.defaultValue || '', 16 | isRequired : options.isRequired || false, 17 | value : options.value || '', 18 | uid : options.uid, 19 | className : options.className 20 | }); 21 | }, 22 | render: function(childIndex) { 23 | 24 | if(!window.Quill) { 25 | window.Quill = Quill; 26 | } 27 | 28 | registerQuillLink(); 29 | 30 | var template = _.template(tpl); 31 | 32 | return template({ 33 | property : this, 34 | childIndex : childIndex 35 | }); 36 | 37 | }, 38 | getDialogValue : function() { 39 | return $('#' + this.id).find('.ql-editor').html(); 40 | } 41 | }); 42 | 43 | return WysiwygProperty; 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_dataSourceSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Data Source settings

    4 |
    5 |
    6 |
    7 |
    8 | 9 | 10 |
    The data source function.
    11 |
    12 | 13 |
    14 | 15 | 16 |
    The data source variable.
    17 |
    18 |
    19 |
    20 | 25 |
    -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/datePicker.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 'rcap/js/ui/properties/textProperty', 2 | 'rcap/js/ui/properties/colorProperty', 'text!rcap/js/ui/controls/child/templates/datePicker.tpl'], function(BaseControl, TextProperty, ColorProperty, tpl) { 3 | 4 | 'use strict'; 5 | 6 | var DatePickerControl = BaseControl.extend({ 7 | init: function() { 8 | this._super({ 9 | type : 'datepicker', 10 | label : 'Date Picker', 11 | icon: 'calendar', 12 | controlProperties: [ 13 | new TextProperty({ 14 | uid: 'label', 15 | label : 'Label', 16 | defaultValue : 'Label', 17 | helpText : 'The label for this control', 18 | isHorizontal: false 19 | }), 20 | new TextProperty({ 21 | uid: 'variablename', 22 | label : 'Variable name', 23 | defaultValue : 'variable', 24 | helpText : 'The variable associated with this control', 25 | isRequired: true, 26 | isHorizontal: false 27 | }) 28 | ] 29 | }); 30 | }, 31 | render: function() { 32 | var template = _.template(tpl); 33 | 34 | return template({ 35 | control: this 36 | }); 37 | } 38 | }); 39 | 40 | return DatePickerControl; 41 | 42 | }); -------------------------------------------------------------------------------- /inst/www/partials/dialogs/templates/executionOrderDetails.tpl: -------------------------------------------------------------------------------- 1 | <% _.forEach(orderDetails, function(page) { %> 2 | <% _.forEach(page.controls, function(control) {%> 3 | 4 | <% if(control.label == 'Form') { %> 5 | <% _.forEach(control.childControls, function(childControl) {%> 6 | 7 | <%= page.navigationTitle %> 8 | Form <%= childControl.label %> 9 | <%= _.filter(childControl.controlProperties, function(prop) { return prop.uid == 'variablename' || prop.uid == 'code' })[0].value %> 10 | 11 | 12 | <% }); %> 13 | <% } else { %> 14 | 15 | <%= page.navigationTitle %> 16 | <%= control.label %> 17 | <%= _.filter(control.controlProperties, function(prop) { return prop.uid == 'variablename' || prop.uid == 'code' })[0].value %> 18 | 19 | 20 | <% } %> 21 | <% }); %> 22 | <% }); %> -------------------------------------------------------------------------------- /inst/www/js/ui/properties/aceProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/ace.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var AceProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type: 'text', 10 | label: options.label || '', 11 | helpText: options.helpText || '', 12 | defaultValue: options.defaultValue || '', 13 | isRequired: options.isRequired || false, 14 | uid: options.uid, 15 | className: options.className 16 | }); 17 | 18 | this.isHorizontal = _.isUndefined(options.isHorizontal) ? true : options.isHorizontal; 19 | }, 20 | render: function(childIndex) { 21 | 22 | var template = _.template(tpl); 23 | 24 | return template({ 25 | property: this, 26 | childIndex: childIndex 27 | }); 28 | 29 | }, 30 | getDialogValue: function() { 31 | //return $('#' + this.id).val(); 32 | 33 | return 'AceProperty.js TODO'; 34 | } 35 | }); 36 | 37 | return AceProperty; 38 | 39 | }); -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_timerSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Timer settings

    4 |
    5 |
    6 |
    7 |
    8 | 9 | 10 |
    The timer variable.
    11 |
    12 |
    13 | 14 | 16 |
    The timer interval (seconds).
    17 |
    18 |
    19 |
    20 | 25 |
    -------------------------------------------------------------------------------- /inst/www/js/ui/properties/dropdownProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/dropdownControl.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var DropdownProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type : 'dropdown', 10 | label : options.label || '', 11 | helpText : options.helpText || '', 12 | defaultValue : options.defaultValue || '', 13 | isRequired : options.isRequired || false, 14 | uid : options.uid, 15 | className : options.className, 16 | value: options.value 17 | }); 18 | 19 | // additional assignments go here: 20 | this.availableOptions = options.availableOptions || []; 21 | this.isHorizontal = _.isUndefined(options.isHorizontal) ? true : options.isHorizontal; 22 | this.defaultOptionText = options.defaultOptionText || 'Select an option'; 23 | }, 24 | render: function(childIndex) { 25 | 26 | var template = _.template(tpl); 27 | 28 | return template({ 29 | property : this, 30 | childIndex : childIndex 31 | }); 32 | 33 | }, 34 | getDialogValue : function() { 35 | return $('#' + this.id).find('option:selected').val(); 36 | } 37 | }); 38 | 39 | return DropdownProperty; 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_pageSettings.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Page Settings

    4 |
    5 |
    6 |
    7 |
    8 | 9 | 10 |
    The name for this page, as it appears in the menu.
    11 |
    12 | 13 |
    14 | 15 | 16 |
    Only enabled pages can be viewed by your website's visitors.
    17 |
    18 |
    19 |
    20 | 25 |
    -------------------------------------------------------------------------------- /inst/www/js/ui/controls/message.js: -------------------------------------------------------------------------------- 1 | define(['pubsub', 2 | 'site/pubSubTable', 3 | 'rcap/js/ui/controls/gridControl', 4 | 'rcap/js/ui/properties/textProperty', 5 | /* 6 | 'rcap/js/ui/properties/stringValueProperty', 7 | 'utils/variableHandler', 8 | */ 9 | 'text!controlTemplates/message.tpl' 10 | ], function (PubSub, pubSubTable, GridControl, TextProperty, /*StringValueProperty, variableHandler,*/ tpl) { 11 | 12 | 'use strict'; 13 | 14 | var MessageControl = GridControl.extend({ 15 | init: function () { 16 | this._super({ 17 | type: 'message', 18 | controlCategory: 'Dynamic', 19 | label: 'Message', 20 | icon: 'comment', 21 | initialSize: [2, 2], 22 | controlProperties: [ 23 | new TextProperty({ 24 | uid: 'variablename', 25 | label: 'Variable', 26 | helpText: 'The variable associated with this message', 27 | isRequired: true 28 | }) 29 | ] 30 | }); 31 | }, 32 | render: function (options) { 33 | options = options || {}; 34 | 35 | var template = _.template(tpl); 36 | 37 | return template({ 38 | control: this, 39 | isDesignTime: options.isDesignTime || false 40 | }); 41 | 42 | }, 43 | initialiseViewerItems: function () { 44 | 45 | } 46 | }); 47 | 48 | return MessageControl; 49 | 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ## Expected Behavior 10 | 11 | 12 | 13 | ## Current Behavior 14 | 15 | 16 | 17 | ## Possible Solution 18 | 19 | 20 | 21 | ## Steps to Reproduce (for bugs) 22 | 23 | 24 | 1. 25 | 2. 26 | 3. 27 | 4. 28 | 29 | ## Context 30 | 31 | 32 | 33 | ## Environment 34 | 35 | * Browser name and version: 36 | -------------------------------------------------------------------------------- /man/topologicalSort.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/topo-sort.R 3 | \name{topologicalSort} 4 | \alias{topologicalSort} 5 | \title{Topological sorting of a directed graph, represented by 6 | adjacency lists. Graph vertices are represented by strings.} 7 | \usage{ 8 | topologicalSort(adjlist) 9 | } 10 | \arguments{ 11 | \item{adjlist}{List of successors of all vertices. 12 | Itmust be named, and the vertex names must be the vertices.} 13 | } 14 | \value{ 15 | The vertex ids, in topologically sorted order. I.e. 16 | if v2 comes after v1 in the list, then there must not be an 17 | edge from v2 to v1. 18 | } 19 | \description{ 20 | It uses Taarjan's depth-first search algorithm, see 21 | https://en.wikipedia.org/wiki/Topological_sorting#Tarjan.27s_algorithm 22 | } 23 | \details{ 24 | \preformatted{ 25 | L <- Empty list that will contain the sorted nodes 26 | while there are unmarked nodes do 27 | select an unmarked node n 28 | visit(n) 29 | 30 | function visit(node n) 31 | if n has a temporary mark then stop (not a DAG) 32 | if n is not marked (i.e. has not been visited yet) then 33 | mark n temporarily 34 | for each node m with an edge from n to m do 35 | visit(m) 36 | mark n permanently 37 | unmark n temporarily 38 | add n to head of L 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /inst/www/js/data/timer.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/Class'], function() { 2 | 3 | 'use strict'; 4 | 5 | var Timer = Class.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this.variable = ''; 9 | this.id = 'rcap' + Math.random().toString(16).slice(2); 10 | this.interval = 60; 11 | }, 12 | start: function() { 13 | var varName = this.variable; 14 | var id = this.id; 15 | 16 | var eventFunction = function(varName, id) { 17 | return function() { 18 | var updateVarsJSON = JSON.stringify({ 19 | updatedVariables:[{ 20 | variableName: varName, 21 | controlId: id, 22 | value: 0 23 | }] 24 | }); 25 | window.RCAP.updateControls(updateVarsJSON); 26 | }; 27 | }; 28 | 29 | setInterval(eventFunction(varName, id), this.interval * 1000); 30 | }, 31 | toJSON: function() { 32 | return { 33 | 'id' : this.id, 34 | 'variable': this.variable, 35 | 'interval': this.interval, 36 | 'type': 'timer' 37 | }; 38 | } 39 | }); 40 | 41 | return Timer; 42 | }); -------------------------------------------------------------------------------- /tests/testthat/test-controls.R: -------------------------------------------------------------------------------- 1 | 2 | context("Controls") 3 | 4 | test_that("controls public API is OK", { 5 | 6 | skip("Cannot test currently without rcloud") 7 | 8 | resp <- paste(readLines("testConfig.json"), collapse = "\n") 9 | resp <- fromJSON(resp, simplifyVector = FALSE) 10 | cnt <- Controller$new(resp) 11 | ctrls <- cnt$.__enclos_env__$private$controls 12 | 13 | expect_equivalent( 14 | vapply(ctrls, function(x) x$getVariableName(), ""), 15 | c("datePicker1", "datePicker2", NA, "option1", NA, NA, NA, NA, NA, 16 | "datePicker3", NA) 17 | ) 18 | }) 19 | 20 | test_that("dependentVariables works", { 21 | 22 | skip("Cannot test currently without rcloud") 23 | 24 | resp <- paste(readLines("testConfig.json"), collapse = "\n") 25 | resp <- fromJSON(resp, simplifyVector = FALSE) 26 | cnt <- Controller$new(resp) 27 | ctrls <- cnt$.__enclos_env__$private$controls 28 | 29 | makePlot1 <- function() { 30 | var1 + var2 31 | } 32 | 33 | expect_equal( 34 | ctrls[[6]]$dependentVariables( 35 | c("var1", "var2", "var3"), 36 | envir = environment() 37 | ), 38 | c("var1", "var2") 39 | ) 40 | 41 | expect_equal( 42 | ctrls[[6]]$dependentVariables(c("var1"), envir = environment()), 43 | "var1" 44 | ) 45 | 46 | expect_equal( 47 | ctrls[[6]]$dependentVariables(character(), envir = environment()), 48 | character() 49 | ) 50 | 51 | }) 52 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rcloud.rcap 2 | Title: Interactive Dashboard and Website Builder for RCloud 3 | Version: 0.4.7.1 4 | Authors@R: c( 5 | person("Shane", "Porter", email = "sporter@mango-solutions.com", 6 | role = c("aut", "cre")), 7 | person("Mateusz", "Rogalski", role = c("aut")), 8 | person("Douglas", "Ashton", role = c("aut")), 9 | person("Gábor", "Csárdi", role = c("aut")), 10 | person("Paulin", "Shek", role = "aut"), 11 | person("Nathan", "Eastwood", role = "aut")) 12 | Description: RCloud powered dashboards and websites. 13 | Depends: 14 | rcloud.htmlwidgets 15 | Imports: 16 | Rserve, 17 | rcloud.support, 18 | rcloud.web, 19 | jsonlite, 20 | R6, 21 | codetools 22 | Suggests: 23 | sankey, 24 | testthat 25 | License: MIT + file LICENSE 26 | RCloud-Extension: gui 27 | RoxygenNote: 5.0.1 28 | SystemRequirements: node.js and npm, bower, Grunt, GNU make 29 | Collate: 30 | 'controlR6.R' 31 | 'dataTableControl.R' 32 | 'controlTypes.R' 33 | 'controllerR6.R' 34 | 'eventHandlerR6.R' 35 | 'getControls.R' 36 | 'ocaps.R' 37 | 'rcap.dataDownload.R' 38 | 'rcap.events.R' 39 | 'rcap.messageWidget.R' 40 | 'rcap.progressSpinner.R' 41 | 'rcap.result.R' 42 | 'rcap.user.profile.R' 43 | 'rcap.upload.data.R' 44 | 'showUpdates.R' 45 | 'styles.R' 46 | 'topo-sort.R' 47 | 'utils.R' 48 | 'zzz.R' 49 | Encoding: UTF-8 50 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/textProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/textControl.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | var TextProperty = BaseProperty.extend({ 6 | init: function(options) { 7 | options = options || {}; 8 | this._super({ 9 | type: 'text', 10 | label: options.label || '', 11 | helpText: options.helpText || '', 12 | defaultValue: options.defaultValue || '', 13 | isRequired: options.isRequired || false, 14 | uid: options.uid, 15 | className: options.className, 16 | value: options.value || options.defaultValue 17 | }); 18 | 19 | // additional assignments go here: 20 | this.validationDataType = options.validationDataType; 21 | 22 | this.isHorizontal = _.isUndefined(options.isHorizontal) ? true : options.isHorizontal; 23 | }, 24 | render: function(childIndex) { 25 | 26 | var template = _.template(tpl); 27 | 28 | return template({ 29 | property: this, 30 | childIndex: childIndex 31 | }); 32 | 33 | }, 34 | getDialogValue: function() { 35 | return $('#' + this.id).val(); 36 | } 37 | }); 38 | 39 | return TextProperty; 40 | 41 | }); -------------------------------------------------------------------------------- /man/Controller.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/controllerR6.R 3 | \docType{data} 4 | \name{Controller} 5 | \alias{Controller} 6 | \title{Orchestrate the updates of the controls in the back-end} 7 | \format{An object of class \code{R6ClassGenerator} of length 24.} 8 | \usage{ 9 | Controller 10 | } 11 | \description{ 12 | Orchestrate the updates of the controls in the back-end 13 | } 14 | \section{Methods}{ 15 | 16 | 17 | \code{initialize(rcapConfig)} The object is constructed from the 18 | parsed designer JSON configuration file. 19 | 20 | \code{update(controls)} update some controls. The argument is the 21 | JSON of the update request from the front-end. 22 | } 23 | 24 | \section{Private methods and variables}{ 25 | 26 | 27 | \code{controls} named list of all controls, \code{Control} objects. 28 | 29 | \code{succList} the adjacency list representation of the control 30 | dependency graph. This is used at initialization to calculate the 31 | update order, and at each update to decide which other controls 32 | must be updated. 33 | 34 | \code{topoSort} ids of all controls, in topological (i.e. update) order. 35 | 36 | \code{updateInOrder(ids)} update the controls in 37 | the specified order, with the specified values. This function is 38 | called to perform the actual updates, once the order is decided 39 | based on the topological sorting. 40 | } 41 | \keyword{datasets} 42 | 43 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/colorProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 2 | 'text!templates/colorControl.tpl', 3 | 'spectrum/spectrum'], function(BaseProperty, tpl) { 4 | 'use strict'; 5 | 6 | var ColorProperty = BaseProperty.extend({ 7 | init: function(options) { 8 | options = options || {}; 9 | this._super({ 10 | type : 'color', 11 | label : options.label || '', 12 | helpText : options.helpText || '', 13 | defaultValue : options.defaultValue || '', 14 | isRequired : options.isRequired || false, 15 | uid : options.uid, 16 | className : options.className 17 | }); 18 | 19 | // additional assignments go here: 20 | this.isHorizontal = _.isUndefined(options.isHorizontal) ? true : options.isHorizontal; 21 | this.showAlpha = _.isUndefined(options.showAlpha) ? false : options.showAlpha; 22 | 23 | // default value dependent on alpha support: 24 | if(this.defaultValue === '') { 25 | this.defaultValue = this.showAlpha ? 'rgba(255, 255, 255, 0)' : 'rgb(255, 255, 255)'; 26 | } 27 | }, 28 | render: function(childIndex) { 29 | 30 | var template = _.template(tpl); 31 | 32 | return template({ 33 | property : this, 34 | childIndex : childIndex 35 | }); 36 | 37 | }, 38 | getDialogValue : function() { 39 | return $('#' + this.id).spectrum('get').toRgbString(); 40 | } 41 | }); 42 | 43 | return ColorProperty; 44 | 45 | }); -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_addPage.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Add New Page

    4 |
    5 |
    6 |
    7 |
    8 | 9 | 10 |
    The name for this page, as it appears in the menu.
    11 |
    12 |
    13 | 14 | 15 |
    The name of this page, as it appears in the top of the browser window.
    16 |
    17 |
    18 | 19 | 20 |
    A unique identifier for the page; leave it blank to use a default
    21 |
    22 |
    23 |
    24 | 28 |
    29 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/dropdownControl.tpl: -------------------------------------------------------------------------------- 1 | <%if(property.isHorizontal) { %> 2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 14 |
    <%=property.helpText%>
    15 |
    16 |
    17 | 18 | <% } else { %> 19 | 20 |
    21 | 22 | 23 | 29 |
    <%=property.helpText%>
    30 |
    31 | 32 | <% } %> 33 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/textControl.tpl: -------------------------------------------------------------------------------- 1 | <%if(property.isHorizontal) { %> 2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 9 | <% if (typeof property.validationDataType !== 'undefined' && property.validationDataType.length > 0) { %> 10 | data-parsley-type="<%= property.validationDataType %>" 11 | <% } %> 12 | /> 13 |
    <%=property.helpText%>
    14 |
    15 |
    16 | 17 | <% } else { %> 18 | 19 |
    20 | 21 | 22 | <% if (typeof property.validationDataType !== 'undefined' && property.validationDataType.length > 0) { %> 23 | data-parsley-type="<%= property.validationDataType %>" 24 | <% } %> 25 | /> 26 |
    <%=property.helpText%>
    27 | 28 |
    29 | 30 | <% } %> -------------------------------------------------------------------------------- /inst/www/js/utils/variableHandler.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/utils/rcapLogger'], function(RcapLogger) { 2 | 3 | 'use strict'; 4 | 5 | return { 6 | submitChange: function(variableData) { 7 | 8 | if(!variableData) { 9 | return; 10 | } 11 | 12 | if(!_.isArray(variableData)) { 13 | variableData = [variableData]; 14 | } 15 | 16 | if(variableData.length) { 17 | var plotSizes = []; 18 | 19 | $('.rplot, .r-interactiveplot, .rhtmlwidget').each(function() { 20 | var container = $(this).closest('.grid-stack-item-content'); 21 | plotSizes.push({ 22 | id : $(this).attr('id'), 23 | width : container.data('width'), 24 | height : container.data('height') 25 | }); 26 | }); 27 | 28 | var dataToSubmit = JSON.stringify({ 29 | updatedVariables : variableData, 30 | plotSizes : plotSizes 31 | }); 32 | 33 | var rcapLogger = new RcapLogger(); 34 | rcapLogger.log('%cJS%c → %cR%c: ' + dataToSubmit, 'color: black; background-color: yellow; font-weight: bold', 'color: black', 'font-weight: bold; color: blue; background-color: #eee', 'color: black'); 35 | 36 | window.RCAP.updateControls(dataToSubmit); 37 | } 38 | 39 | } 40 | }; 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /man/getControls.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getControls.R 3 | \name{getControls} 4 | \alias{getControls} 5 | \title{Return a list of all controls} 6 | \usage{ 7 | getControls(rcapConfig) 8 | } 9 | \arguments{ 10 | \item{rcapConfig}{a list generated by 11 | \code{jsonlite::fromJSON}, with \code{simplifyVector = TRUE}.} 12 | } 13 | \value{ 14 | List of controls (each a list itself). 15 | } 16 | \description{ 17 | The pages of the dashboard and the controls form a hierarchy, 18 | as controls can have child controls. This parser goes over the 19 | hierarchy recursively, and returns a flat list containing 20 | all controls. 21 | } 22 | \details{ 23 | Each list element corresponds to a control, it is a list itself, 24 | with the followinf members: 25 | \itemize{ 26 | \item \code{type} see \code{control_classes} for the types. 27 | \item \code{x}, \code{y} position on the grid. 28 | \item \code{width}, \code{height} size on te grid. 29 | \item \code{id} unique identifier, this is used in the protocol. 30 | \item \code{controlProperties} another list, with \code{uid}, 31 | code{value} pairs, containing the configuration of the control. 32 | (E.g. labels, associated R variables, or R function, etc.) 33 | \item \code{childControls} if a control has children, they are 34 | recursively included here. 35 | } 36 | 37 | See \preformatted{ system.file("tests", "testthat", 38 | "testConfig.json", package = "rcloud.rcap") 39 | } for an example JSON file describing controls. 40 | } 41 | 42 | -------------------------------------------------------------------------------- /inst/www/js/versionConverters/versionConverter.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'pubsub', 'site/pubSubTable', 3 | 'text!rcap/js/versionConverters/legacyThemes/att.css', 4 | 'text!rcap/js/versionConverters/legacyThemes/bragi.css' 5 | ], function(PubSub, pubSubTable, themeAtt, themeBragi) { 6 | 7 | 'use strict'; 8 | 9 | var VersionConverter = function() { 10 | 11 | var converters = [{ 12 | from: undefined, 13 | to: 1.0, 14 | convert: function(json) { 15 | if(['bragi', 'att'].indexOf(json.theme) !== -1) { 16 | PubSub.publish(pubSubTable.updateTheme, json.theme === 'bragi' ? themeBragi : themeAtt); 17 | } 18 | json.theme = undefined; 19 | 20 | return json; 21 | } 22 | }, 23 | 24 | // add more converters here: 25 | 26 | ]; 27 | 28 | this.processJson = function(json) { 29 | 30 | var convertedJson = json; 31 | 32 | _.each(converters, function(converter) { 33 | 34 | if(_.isUndefined(convertedJson.version) && _.isUndefined(converter.from) || 35 | convertedJson.version && converter.from && convertedJson.version === converter.from) { 36 | convertedJson = converter.convert(convertedJson); 37 | convertedJson.vesion = converter.to; 38 | } 39 | }); 40 | 41 | return convertedJson; 42 | }; 43 | 44 | }; 45 | 46 | return VersionConverter; 47 | 48 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/controls/interactivePlot.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/gridControl', 2 | 'rcap/js/ui/properties/textProperty', 3 | 'rcap/js/ui/properties/autocompleteProperty', 4 | 'text!controlTemplates/interactivePlot.tpl', 5 | 'text!controlTemplates/interactivePlot-design.tpl' 6 | ], function(GridControl, TextProperty, AutocompleteProperty, tpl, dtpl) { 7 | 8 | 'use strict'; 9 | 10 | var InteractivePlotControl = GridControl.extend({ 11 | init: function() { 12 | this._super({ 13 | type: 'interactiveplot', 14 | controlCategory: 'Dynamic', 15 | label: 'Interactive Plot', 16 | icon: 'bar-chart', 17 | controlProperties: [ 18 | new AutocompleteProperty({ 19 | uid: 'code', 20 | label: 'R Function', 21 | helpText: 'R Function for this control.', 22 | isRequired: true 23 | }) 24 | ] 25 | }); 26 | }, 27 | render: function(options) { 28 | 29 | options = options || {}; 30 | var isDesignTime = options.isDesignTime || false; 31 | 32 | var template = isDesignTime ? _.template(dtpl) : _.template(tpl); 33 | 34 | return template({ 35 | control: this 36 | }); 37 | 38 | }, 39 | initialiseViewerItems: function() { 40 | 41 | } 42 | }); 43 | 44 | return InteractivePlotControl; 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /inst/www/styles/jqModal/jqModal.scss: -------------------------------------------------------------------------------- 1 | /* jqModal base Styling courtesy of; 2 | Brice Burgess */ 3 | 4 | /* The Window's CSS z-index value is respected (takes priority). If none is supplied, 5 | the Window's z-index value will be set to 3000 by default (via jqModal.js). */ 6 | 7 | .jqmWindow { 8 | display: none; 9 | 10 | position: fixed; 11 | top: 17%; 12 | left: 50%; 13 | 14 | margin-left: -800px; 15 | min-width: 600px; 16 | max-width: 80%; 17 | 18 | background-color: #EEE; 19 | color: #333; 20 | border: 1px solid black; 21 | padding: 12px; 22 | 23 | max-height: calc(100% - 23px); 24 | 25 | @include noselect(); 26 | } 27 | 28 | .jqmWindow .body { 29 | max-height: calc(100% - 102px); 30 | overflow: auto; 31 | } 32 | 33 | .jqmOverlay { background-color: #000; } 34 | 35 | /* Background iframe styling for IE6. Prevents ActiveX bleed-through ( 13 | 14 | <% if(control.getPropertyValue('intervalType') != '') { %> 15 | 16 | plus 17 | 18 | <%=control.singularInterval()%> 19 | 20 | 38 | 39 | <% } else { %> 40 | to 41 | <% } %> 42 | 43 | -------------------------------------------------------------------------------- /inst/www/partials/dialogs/templates/viewerDataUpload.tpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | (Please only use the following characters: letters, digits, hyphens and underscores) 5 |
    6 |
    7 |
    8 |
    9 | 10 |
    11 |
    12 | 13 |
    14 |
    15 | 16 |
    17 |
    18 |
    19 |
    20 | 21 |
    22 |
    23 | 24 |
    25 |
    26 | 27 | (Please only use the following characters: letters, digits, spaces, hyphens and underscores) 28 |
    29 |
    30 |
    31 |
    32 |
    33 |
    34 |
    35 | 36 | 39 |
    40 |
    41 |
    42 |
    43 |
    -------------------------------------------------------------------------------- /inst/www/js/utils/historyManager.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'rcap/js/utils/rcapLogger', 3 | 'pubsub', 4 | 'site/pubSubTable', 5 | 'text!rcap/partials/_404.htm' 6 | ], function(RcapLogger, PubSub, pubSubTable, pageNotFoundPartial) { 7 | 8 | 'use strict'; 9 | 10 | var rcapLogger = new RcapLogger(); 11 | 12 | var HistoryManager = function() { 13 | 14 | var pageNotFoundSelector = '#viewer-404'; 15 | 16 | this.initialise = function() { 17 | 18 | // append the '404': 19 | $('#inner-stage').append(''); 20 | 21 | window.addEventListener('popstate', function( /*e*/ ) { 22 | rcapLogger.log('history manager: popstate'); 23 | }); 24 | 25 | window.onhashchange = function() { 26 | 27 | // assume that this will be found: 28 | $(pageNotFoundSelector).hide(); 29 | 30 | PubSub.publish(pubSubTable.changeSelectedPageByTitle, window.unescape(location.hash.substring(1))); 31 | }; 32 | 33 | PubSub.subscribe(pubSubTable.show404, function(msg, data) { 34 | var template = _.template(pageNotFoundPartial); 35 | $(pageNotFoundSelector).html(template({ 36 | pages : data.site.pages, 37 | requestedPage : data.requestedPage 38 | })).show(); 39 | }); 40 | 41 | }; 42 | 43 | this.setInitialState = function() { 44 | if (location.hash.length) { 45 | // show the specific 'page': 46 | PubSub.publish(pubSubTable.changeSelectedPageByTitle, window.unescape(location.hash.substring(1))); 47 | } else { 48 | // the first is as good as any: 49 | PubSub.publish(pubSubTable.viewerShowFirstPage); 50 | } 51 | }; 52 | 53 | }; 54 | 55 | return HistoryManager; 56 | 57 | }); -------------------------------------------------------------------------------- /inst/www/partials/dialogs/templates/viewerProfileVariables.tpl: -------------------------------------------------------------------------------- 1 | 2 | <% if(hasStaleItem) { %> 3 |
    4 | <% _.each(profileDataItems, function(o, i){ %> 5 | <% if(o.staleValues.length) { %> 6 |

    7 | Your saved selection (<% _.each(o.staleValues, function(staleValue) { %> 8 | <%=staleValue%> 9 | <% }); %>) for variable '<%=o.description%>' <%= (o.allStale) ? ' was not found in the currently available values.' : ' has some values that are no longer current.' %> 10 |

    11 | <% } %> 12 | <% }); %> 13 |
    14 | <% } %> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | <% _.each(profileDataItems, function(o, i){ %> 26 | 27 | 28 | 34 | 45 | 46 | <% }); %> 47 | 48 |
    VariableSelection methodValues
    <%=o.description%> 29 | 33 | 35 |
    ">All
    36 |
    > 37 | 42 |
    43 |
    44 |
    49 | -------------------------------------------------------------------------------- /inst/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RCAP", 3 | "version": "0.0.0", 4 | "description": "RCAP for RCloud", 5 | "keywords": [ 6 | "rcloud", 7 | "r" 8 | ], 9 | "authors": [ 10 | "Shane Porter", 11 | "Douglas Ashton" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ], 21 | "dependencies": { 22 | "requirejs-plugins": "~1.0.3", 23 | "requirejs-text": "~2.0.14", 24 | "select2": "~4.0.0", 25 | "spectrum": "~1.8", 26 | "ionrangeslider": "~2.1.2", 27 | "datatables.net": "~1.10.10", 28 | "datatables.net-buttons": "~1.2.1", 29 | "datatables.net-buttons-dt": "~1.2.1", 30 | "jquery.sparkline": "~2.1.2", 31 | "datatables.net-fixedheader": "~3.1.2", 32 | "datatables.net-fixedheader-dt": "~3.1.2", 33 | "pubsub-js": "~1.5.2", 34 | "parsleyjs": "~2.7.2", 35 | "jquery.hotkeys": "*", 36 | "require-css": "~0.1.8" 37 | }, 38 | "overrides": { 39 | "jquery.sparkline": { 40 | "main": "dist/jquery.sparkline.min.js" 41 | }, 42 | "parsleyjs": { 43 | "main": "dist/parsley.min.js" 44 | }, 45 | "datatables.net": { 46 | "main": ["js/jquery.dataTables.min.js", 47 | "js/jquery.dataTables.js" 48 | ] 49 | }, 50 | "datatables.net-buttons": { 51 | "main": ["js/dataTables.buttons.js", 52 | "js/buttons.colVis.js", 53 | "js/buttons.flash.js", 54 | "js/buttons.html5.min.js", 55 | "js/buttons.print.js" 56 | ] 57 | }, 58 | "select2": { 59 | "main": [ 60 | "dist/js/select2.js", "dist/css/select2.min.css" 61 | ] 62 | }, 63 | "ionrangeslider": { 64 | "main": [ 65 | "js/ion.rangeSlider.js", 66 | "css/ion.rangeSlider.css", 67 | "css/ion.rangeSlider.skinFlat.css", 68 | "css/ion.rangeSlider.skinHTML5.css", 69 | "img/sprite-skin-flat.png" 70 | ] 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/templates/dataTable.tpl: -------------------------------------------------------------------------------- 1 | 2 |
    3 | 4 | <%if(designTimeDescription.length && isDesignTime) {%> 5 |
    <%=designTimeDescription%>
    6 | <%}%> 7 | 8 | <% if(isDesignTime) { %> 9 | 30 | <% } %> 31 | 32 | 44 | -------------------------------------------------------------------------------- /R/rcap.messageWidget.R: -------------------------------------------------------------------------------- 1 | #' @title Write message to message widget with specified id 2 | #' 3 | #' @param variableName the name of the variable linked with message widget control 4 | #' @param message the message that should be displayed 5 | #' @param append should the message be appended 6 | #' @return NULL invisibly 7 | #' @export 8 | rcap.messageWidget.write <- function(variableName, message, append=FALSE) { 9 | if(haveController()) { 10 | cnt <- get("rcapController", envir = rcapEnv) 11 | control <- Filter(function(x) {x$getVariableName()==variableName}, cnt$getControls()) 12 | if(length(control)!=1) { 13 | stop(paste0("Control for variable '", variableName, "' not found!")); 14 | } 15 | control <- control[[1]] 16 | event <- list("eventType" = "MessageWidgetWrite", 17 | "controlId" = control$getId(), 18 | "data" = list("append" = append, "message" = message) 19 | ) 20 | response <- rcap.events.send(event) 21 | if(response$status != 'Success') { 22 | warning(paste0("Event processing failed ", response$msg)) 23 | } 24 | } else { 25 | warning("Writting to message widget is only available in dashboard view.") 26 | } 27 | invisible(NULL) 28 | } 29 | 30 | #' @title Write message to a collection of Message widgets 31 | #' 32 | #' @param msg named list of messages, with keys being Message widget variableNames 33 | #' @param append flag specifying if the messages should be appended to widgets' content or not 34 | #' @param ... named parameters, if 'msg' is not defined, these specify progress spinners and messages that should be written. 35 | #' @return NULL invisibly 36 | #' @export 37 | rcap.messageWidget.msg <- function(msg=NULL, append=FALSE, ...) { 38 | parms <- list(...) 39 | if(is.null(msg) && length(parms) >0 ) { 40 | msg <- parms 41 | } 42 | if( length(msg) > 0 ) { 43 | msg <- as.list(msg) 44 | for(p in 1:length(msg)) { 45 | if(!is.na(names(msg)[[p]])) { 46 | rcloud.rcap:::rcap.messageWidget.write(names(msg)[[p]], msg[[p]], append = append) 47 | } 48 | } 49 | } 50 | invisible(NULL) 51 | } -------------------------------------------------------------------------------- /inst/www/js/ui/controls/actionButton.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/gridControl', 2 | 'rcap/js/ui/properties/textProperty', 3 | 'utils/variableHandler', 4 | 'text!controlTemplates/actionButton.tpl' 5 | ], function(GridControl, TextProperty, variableHandler, tpl) { 6 | 7 | 'use strict'; 8 | 9 | var RPlotControl = GridControl.extend({ 10 | init: function() { 11 | this._super({ 12 | type: 'actionbutton', 13 | controlCategory: 'Dynamic', 14 | label: 'Action Button', 15 | icon: 'exclamation-sign', 16 | initialSize: [2, 2], 17 | controlProperties: [ 18 | new TextProperty({ 19 | uid: 'variablename', 20 | label: 'Variable', 21 | helpText: 'The variable associated with this control', 22 | isRequired: true 23 | }), 24 | new TextProperty({ 25 | uid: 'buttonText', 26 | label : 'Button Text', 27 | defaultValue : '', 28 | helpText : 'The text that appears on the button', 29 | isRequired: true 30 | }) 31 | ] 32 | }); 33 | }, 34 | getVariableData: function() { 35 | 36 | }, 37 | render: function() { 38 | 39 | var template = _.template(tpl); 40 | 41 | return template({ 42 | control: this 43 | }); 44 | 45 | }, 46 | initialiseViewerItems: function() { 47 | 48 | $('[data-controltype="actionbutton"]').click(function() { 49 | variableHandler.submitChange({ 50 | variableName: $(this).attr('data-variablename'), 51 | controlId: $(this).attr('id'), 52 | value: Math.random().toString(16).slice(2).substring(0, 6) 53 | }); 54 | }); 55 | } 56 | }); 57 | 58 | return RPlotControl; 59 | 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /inst/www/js/utils/pageWalker.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | 3 | 'use strict'; 4 | 5 | return function(pages) { 6 | 7 | this.findPage = function(pageId) { 8 | return _.findWhere(pages, { 9 | id: pageId 10 | }); 11 | }; 12 | 13 | this.getDescendantsAndSelf = function(rootPageId) { 14 | 15 | var traversedPages = []; 16 | 17 | var walkPages = function(subsetOfPages) { 18 | 19 | _.each(subsetOfPages, function(p) { 20 | 21 | traversedPages.push(p); 22 | 23 | var childPages = _.filter(pages, function(sp) { 24 | return sp.parentId === p.id; 25 | }); 26 | 27 | if (childPages.length > 0) { 28 | walkPages(childPages); 29 | } 30 | }); 31 | }; 32 | 33 | walkPages(_.filter(pages, function(p) { 34 | return rootPageId ? p.id === rootPageId : !p.parentId; 35 | })); 36 | 37 | return traversedPages; 38 | 39 | }; 40 | 41 | this.getAncestorsAndSelf = function(pageId) { 42 | 43 | var foundPage, treeItems = []; 44 | 45 | do { 46 | foundPage = _.findWhere(pages, { 47 | id: pageId 48 | }); 49 | pageId = foundPage.parentId; 50 | 51 | treeItems.unshift(foundPage); 52 | 53 | } while (foundPage && pageId); 54 | 55 | return treeItems; 56 | }; 57 | 58 | this.removePage = function(pageId) { 59 | 60 | var pageIds = _.pluck(this.getDescendantsAndSelf(pageId), 'id'); 61 | 62 | return _.filter(pages, function(p) { 63 | return pageIds.indexOf(p.id) === -1; 64 | }); 65 | }; 66 | 67 | this.setPageEnabledStatus = function(pageId, isEnabled) { 68 | 69 | _.each(this.getDescendantsAndSelf(), function(p) { 70 | p.isEnabled = isEnabled; 71 | }); 72 | 73 | }; 74 | 75 | }; 76 | 77 | }); 78 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/heading.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 'rcap/js/ui/properties/textProperty', 2 | 'rcap/js/ui/properties/dropdownProperty', 3 | 'text!rcap/js/ui/controls/child/templates/heading.tpl'], function(BaseControl, TextProperty, DropdownProperty, tpl) { 4 | 5 | 'use strict'; 6 | 7 | var HeadingControl = BaseControl.extend({ 8 | init: function() { 9 | this._super({ 10 | type: 'heading', 11 | label: 'Heading', 12 | icon: 'font', 13 | controlProperties: [ 14 | new TextProperty({ 15 | uid: 'text', 16 | label: 'Text', 17 | defaultValue: 'Heading', 18 | isHorizontal: false 19 | }), 20 | new DropdownProperty({ 21 | uid: 'testdropdown', 22 | label: 'Heading type', 23 | helpText: 'Heading size', 24 | isRequired: true, 25 | availableOptions: [{ 26 | text: 'Largest', 27 | value: 'h1' 28 | }, { 29 | text: 'Large', 30 | value: 'h2' 31 | }, { 32 | text: 'Medium', 33 | value: 'h3' 34 | }, { 35 | text: 'Small', 36 | value: 'h4' 37 | }, { 38 | text: 'Smallest', 39 | value: 'h5' 40 | }], 41 | value: 'h1', // default to the largest 'h1' 42 | isHorizontal: false 43 | }) 44 | ] 45 | }); 46 | }, 47 | render: function() { 48 | var template = _.template(tpl); 49 | 50 | return template({ 51 | control: this 52 | }); 53 | } 54 | }); 55 | 56 | return HeadingControl; 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/wysiwyg.tpl: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
    43 |
    44 | <%=property.value%> 45 |
    46 |
    47 |
    48 | 49 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/colorControl.tpl: -------------------------------------------------------------------------------- 1 | <%if(property.isHorizontal) { %> 2 | 3 |
    4 |
    5 | 6 |
    7 |
    8 | 9 |
    <%=property.helpText%>
    10 | 26 |
    27 |
    28 | 29 | <% } else { %> 30 | 31 |
    32 | 33 |
    34 | 35 |
    36 |
    <%=property.helpText%>
    37 | 53 |
    54 | 55 | <% } %> 56 | -------------------------------------------------------------------------------- /inst/www/js/site/siteSettings.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/textProperty', 'rcap/js/ui/properties/dropdownProperty'], function(TextProperty, DropdownProperty) { 2 | 3 | 'use strict'; 4 | 5 | var SiteSettings = Class.extend({ 6 | init: function() { 7 | this.properties = [ 8 | new DropdownProperty({ 9 | uid: 'gridControlPadding', 10 | label: 'Grid Control Padding', 11 | isRequired: false, 12 | availableOptions: _.map([0, 5, 10, 20], function(val) { return { 13 | text: val.toString(), 14 | value: val.toString() 15 | }; 16 | }), 17 | value: '20', 18 | helpText: 'The amount of padding around each control (in pixels)' 19 | }), 20 | new TextProperty({ 21 | uid: 'pageClass', 22 | label : 'Page class', 23 | defaultValue : '', 24 | helpText : 'Apply a CSS class to each page', 25 | isRequired: false, 26 | }), 27 | new DropdownProperty({ 28 | uid: 'siteThemePackage', 29 | label: 'Site Theme Package', 30 | isRequired: false, 31 | availableOptions: window.RCAP.getRCAPStyles ? window.RCAP.getRCAPStyles().map(function(style) { 32 | return { 33 | text: style.package, 34 | value: style.package 35 | }; 36 | }) : [], 37 | helpText: 'Custom theme' 38 | }) 39 | ]; 40 | }, 41 | getSettingValue: function(uid) { 42 | return _.findWhere(this.properties, { 43 | uid: uid 44 | }).value; 45 | }, 46 | extract: function() { 47 | var obj = {}; 48 | 49 | this.properties.forEach(function(prop) { 50 | obj[prop.uid] = prop.value; 51 | }); 52 | 53 | return obj; 54 | } 55 | }); 56 | 57 | return SiteSettings; 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /tests/testthat/testConfig.json: -------------------------------------------------------------------------------- 1 | {"saveTicks":1445253670794,"pages":[{"depth":1,"id":"rcapda189c91","navigationTitle":"Home","isEnabled":true,"controls":[{"type":"form","x":0,"y":1,"width":4,"height":3,"id":"rcapd77d04dd","controlProperties":[],"childControls":[{"type":"datepicker","id":"rcap16014ed1","controlProperties":[{"uid":"label","value":"Start Date","id":"ctrltext03c6f82b"},{"uid":"variablename","value":"datePicker1","id":"ctrltextff6451db"}]},{"type":"datepicker","id":"rcapc43838c4","controlProperties":[{"uid":"label","value":"End Date","id":"ctrltext00b111e6"},{"uid":"variablename","value":"datePicker2","id":"ctrltextb81ce3cc"}]},{"type":"separator","id":"rcapd2b608e4","controlProperties":[]},{"type":"checkboxlist","id":"rcap1cc91a8e","controlProperties":[{"uid":"description","value":"Some Options","id":"ctrltext842f058a"},{"uid":"variablename","value":"option1","id":"ctrltexte02aea9e"},{"uid":"options","value":[{"label":"Option 1","value":""},{"label":"Option 2","value":""}],"id":"ctrlmultioptionf2691ded"}]}]},{"type":"rplot","x":4,"y":1,"width":5,"height":4,"id":"rcap630974bf","controlProperties":[{"uid":"code","value":"makePlot1","id":"ctrlmultilinetext4039fea7"}],"isOnGrid":true},{"type":"image","x":0,"y":4,"width":4,"height":3,"id":"rcap08ca0d2f","controlProperties":[{"uid":"imagesource","value":"http://www.dougashton.net/pics/profpic.jpg","id":"ctrltext6f623218"},{"uid":"imageLayout","value":"background-size: cover;","id":"ctrldropdowne3054be5"}],"isOnGrid":true}]},{"depth":1,"id":"rcap961dec28","navigationTitle":"Second Page","isEnabled":true,"controls":[{"type":"rplot","x":0,"y":1,"width":6,"height":4,"id":"rcapcea65234","controlProperties":[{"uid":"code","value":"makePlot2","id":"ctrlmultilinetext2f3c4efc"}],"isOnGrid":true}]},{"depth":2,"id":"rcap2282b1ae","parentId":"rcap961dec28","navigationTitle":"Child of Second Page","isEnabled":true,"controls":[{"type":"rplot","x":0,"y":1,"width":5,"height":4,"id":"rcapc4281584","controlProperties":[{"uid":"code","value":"makePlotChild2","id":"ctrlmultilinetext4255d1a5"}],"isOnGrid":true},{"type":"form","x":5,"y":1,"width":5,"height":4,"id":"rcap6d1badca","controlProperties":[],"childControls":[{"type":"datepicker","id":"rcapa9b893f5","controlProperties":[{"uid":"label","value":"Third Date","id":"ctrltext6c484784"},{"uid":"variablename","value":"datePicker3","id":"ctrltext53419aee"}]}]}]}]} 2 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/templates/multiOptionControl.tpl: -------------------------------------------------------------------------------- 1 |
    2 |

    <%=property.label%>

    3 | 4 | 5 | 6 | 7 | > 8 | 9 | 10 | > 11 | 12 | 13 | 14 | 15 | 19 | 20 | 24 | 25 | 40 |
    -------------------------------------------------------------------------------- /inst/www/js/ui/dialogUtils.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/vendor/jqModal.min'], function () { 2 | 3 | 'use strict'; 4 | 5 | var DialogUtils = function() { 6 | this.initialise = function () { 7 | 8 | var sizeModalBodyHeight = function (modal) { 9 | 10 | var availableHeight = $(/*document*/window).height() - 165; 11 | var maxHeight = modal.find('.body').data('maxheight'); 12 | 13 | var $modalBody = modal.find('.body'); 14 | 15 | if (!maxHeight) { 16 | var initialHeight = modal.find('.body').height(); 17 | 18 | if (initialHeight > availableHeight) { 19 | $modalBody.height(availableHeight + 'px'); 20 | } else { 21 | $modalBody.height(initialHeight + 'px'); 22 | } 23 | 24 | modal.find('.body').data('maxheight', initialHeight); 25 | 26 | } else { 27 | 28 | if (maxHeight === 'useavailable') { 29 | $modalBody.height(availableHeight + 'px'); 30 | } else { 31 | if (availableHeight > maxHeight) { 32 | $modalBody.height(maxHeight + 'px'); 33 | } else { 34 | $modalBody.height(availableHeight + 'px'); 35 | } 36 | } 37 | } 38 | }; 39 | 40 | // initialise each of the dialogs: 41 | $('.jqmWindow').each(function () { 42 | $(this).jqm({ 43 | modal: true, 44 | onShow: function (hash) { 45 | 46 | // display the overlay (prepend to body) if not disabled 47 | if (hash.c.overlay > 0) { 48 | hash.o.prependTo('body'); 49 | } 50 | 51 | // make modal visible 52 | hash.w.show(); 53 | 54 | // call focusFunc (attempts to focus on first input in modal) 55 | $.jqm.focusFunc(hash.w, null); 56 | 57 | sizeModalBodyHeight(hash.w); 58 | 59 | return true; 60 | }, 61 | onHide: function (hash) { 62 | 63 | // hide modal and if overlay, remove overlay. 64 | hash.w.hide(); 65 | hash.o.remove(); 66 | 67 | hash.w.find('body').removeData('maxheight'); 68 | 69 | return true; 70 | } 71 | }); 72 | }); 73 | 74 | $(window).resize(function () { 75 | sizeModalBodyHeight($('.jqmWindow:visible')); 76 | }); 77 | }; 78 | }; 79 | 80 | return DialogUtils; 81 | }); 82 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/dropdown.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 'rcap/js/ui/properties/textProperty', 2 | 'rcap/js/ui/properties/multiOptionProperty', 'rcap/js/ui/properties/dropdownProperty', 'text!rcap/js/ui/controls/child/templates/dropdown.tpl'], 3 | function(BaseControl, TextProperty, MultiOptionProperty, DropdownProperty, tpl) { 4 | 5 | 'use strict'; 6 | 7 | var DropdownControl = BaseControl.extend({ 8 | init: function() { 9 | this._super({ 10 | type : 'dropdown', 11 | label : 'Dropdown', 12 | icon: 'list', 13 | controlProperties: [ 14 | new TextProperty({ 15 | uid: 'label', 16 | label : 'Label', 17 | defaultValue : 'Label', 18 | helpText : 'The label for this control', 19 | isHorizontal: false 20 | }), 21 | new TextProperty({ 22 | uid: 'variablename', 23 | label : 'Variable name', 24 | defaultValue : 'variable', 25 | helpText : 'The variable associated with this control', 26 | isRequired: true, 27 | isHorizontal: false 28 | }), 29 | // options: 30 | new MultiOptionProperty({ 31 | uid: 'options', 32 | label: 'Options', 33 | helpText: 'Enter options, one per line', 34 | value: [{ 35 | label: 'Option 1', 36 | value: '1' 37 | }, { 38 | label: 'Option 2', 39 | value: '2' 40 | }], 41 | isRequired: true, 42 | isHorizontal: false 43 | }), 44 | // selection style (default or dialog) 45 | new DropdownProperty({ 46 | uid: 'selectionStyle', 47 | label: 'Selection style', 48 | helpText: 'Use default style or show options in a dialog', 49 | isRequired: false, 50 | availableOptions: [{ 51 | text: 'Dialog', 52 | value: 'dialog' 53 | }], 54 | defaultOptionText: 'Default', 55 | isHorizontal: false 56 | }), 57 | ] 58 | }); 59 | }, 60 | render: function(options) { 61 | 62 | options = options || {}; 63 | 64 | var template = _.template(tpl); 65 | 66 | return template({ 67 | control: this, 68 | isDesignTime: options.isDesignTime || false 69 | }); 70 | 71 | } 72 | }); 73 | 74 | return DropdownControl; 75 | 76 | }); 77 | -------------------------------------------------------------------------------- /tests/testthat/test-topo-sort.R: -------------------------------------------------------------------------------- 1 | 2 | context("Topological sorting") 3 | 4 | test_that("toplogical sorting works", { 5 | 6 | G <- list( 7 | "7" = c("11", "8"), 8 | "5" = "11", 9 | "3" = c("8", "10"), 10 | "11" = c("2", "9", "10"), 11 | "8" = "9", 12 | "2" = character(), 13 | "9" = character(), 14 | "10" = character() 15 | ) 16 | 17 | topo <- topologicalSort(G) 18 | 19 | expect_true(match("7", topo) < match("11", topo)) 20 | expect_true(match("7", topo) < match("8", topo)) 21 | expect_true(match("5", topo) < match("11", topo)) 22 | expect_true(match("3", topo) < match("8", topo)) 23 | expect_true(match("3", topo) < match("10", topo)) 24 | expect_true(match("11", topo) < match("2", topo)) 25 | expect_true(match("11", topo) < match("9", topo)) 26 | expect_true(match("11", topo) < match("10", topo)) 27 | expect_true(match("8", topo) < match("9", topo)) 28 | 29 | }) 30 | 31 | test_that("topological sorting works on sparse graphs", { 32 | 33 | G <- list( 34 | "a" = "b", 35 | "b" = character(), 36 | "c" = "d", 37 | "d" = character(), 38 | "e" = "f", 39 | "f" = character() 40 | ) 41 | 42 | topo <-topologicalSort(G) 43 | 44 | expect_true(match("a", topo) < match("b", topo)) 45 | expect_true(match("c", topo) < match("d", topo)) 46 | expect_true(match("e", topo) < match("f", topo)) 47 | 48 | topo2 <- topologicalSort( 49 | list("a" = "b", "b" = "c", "c" = "d", "d" = "e", "e" = character()) 50 | ) 51 | 52 | expect_equal(topo2, c("a", "b", "c", "d", "e")) 53 | 54 | topo3 <- topologicalSort(list("a" = character(), "b" = character())) 55 | 56 | expect_equal(sort(topo3), c("a", "b")) 57 | 58 | }) 59 | 60 | test_that("twistAdjlist works", { 61 | 62 | G1 <- list( 63 | "7" = c("11", "8"), 64 | "5" = "11", 65 | "3" = c("8", "10"), 66 | "11" = c("2", "9", "10"), 67 | "8" = "9", 68 | "2" = character(), 69 | "9" = character(), 70 | "10" = character() 71 | ) 72 | 73 | G11 <- twistAdjlist(G1) 74 | 75 | expect_equal(names(G11), names(G1)) 76 | expect_equal( 77 | G11, 78 | list( 79 | "7" = character(), 80 | "5" = character(), 81 | "3" = character(), 82 | "11" = c("7", "5"), 83 | "8" = c("7", "3"), 84 | "2" = "11", 85 | "9" = c("11", "8"), 86 | "10" = c("3", "11") 87 | ) 88 | ) 89 | 90 | G111 <- twistAdjlist(G11) 91 | expect_equal(G1, G111) 92 | }) 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # RCAP 3 | 4 | ## Introduction 5 | 6 | RCAP is an Interactive Dashboard Builder extension for [RCloud](https://github.com/att/rcloud#readme). 7 | An RCAP dashboard is an RCloud asset associated with an RCloud notebook. Use RCAP to create and edit 8 | the dashboard, and `rcap.html` mode to view and share it. 9 | 10 | [![RCAP Example on YouTube](https://github.com/att/rcloud.rcap/blob/develop/youtube.png)](http://www.youtube.com/watch?v=h9ErbyvD_FA) 11 | 12 | ## Building 13 | 14 | Build has the following environment dependencies: 15 | 16 | * `node.js` & `npm`, `bower`, 17 | * `grunt` and `GNU make`. 18 | 19 | To build RCAP package run the following command from package source: 20 | 21 | 22 | git clone https://github.com/att/rcloud.rcap 23 | cd rcloud.rcap 24 | sh ./mkdist 25 | 26 | The 'mkdist' script accepts a number of optional parameters useful during development: 27 | 28 | ```sh 29 | Usage: ./mkdist [{--no-npm | --no-js | --no-clean > | --help}] [install] 30 | --no-clean - skips clean phase 31 | --no-npm - skips npm processing step (implies --no-clean option) 32 | --no-js - skips entire JavaScript processing step (implies --no-npm option) 33 | --help - displays usage information 34 | 35 | --install - should package be installed after it has been built. 36 | ``` 37 | 38 | 39 | ## Installation 40 | 41 | ### RCAP bundle 42 | 43 | It is possible to install RCAP from R package bundle, which contains all JS dependencies. 44 | 45 | ```sh 46 | wget 'https://github.com/att/rcloud.rcap/releases/download/0.4.7.1/rcloud.rcap_0.4.7.1.tar.gz' 47 | R CMD INSTALL rcloud.rcap_0.4.7.1.tar.gz 48 | ``` 49 | 50 | 51 | ### devtools 52 | 53 | To install the latest released build, you can use the `devtools` package: 54 | ```r 55 | devtools::install_url( 56 | "https://github.com/att/rcloud.rcap/releases/download/0.4.7.1/rcloud.rcap_0.4.7.1.tar.gz" 57 | ) 58 | ``` 59 | You can also run this from within an RCloud notebook, assuming you have 60 | access rights to install R packages on the RCloud server. 61 | 62 | To install the development version, use 63 | ```r 64 | devtools::install_github("att/rcloud.rcap@develop", local = FALSE) 65 | ``` 66 | 67 | ## Configuration 68 | 69 | After installing the R package, you can enable RCAP in RCloud in the 70 | `Settings` menu (on the bottom left). Add `rcloud.rcap` to the 71 | `Enable Extensions` line, and reload the notebook. 72 | 73 | You can start the designer from the `Advanced` menu. 74 | 75 | ## License 76 | 77 | MIT @ AT&T 78 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/checkboxList.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 2 | 'rcap/js/ui/properties/textProperty', 3 | 'rcap/js/ui/properties/multiOptionProperty', 4 | 'text!rcap/js/ui/controls/child/templates/checkboxList.tpl' 5 | ], function(BaseControl, TextProperty, MultiOptionProperty, tpl) { 6 | 7 | 'use strict'; 8 | 9 | var CheckboxListControl = BaseControl.extend({ 10 | init: function() { 11 | 12 | this._super({ 13 | type : 'checkboxlist', 14 | label : 'Checkbox Group', 15 | icon: 'check', 16 | controlProperties: [ 17 | new TextProperty({ 18 | uid: 'description', 19 | label: 'Description', 20 | defaultValue: 'Description', 21 | helpText: 'Instructions / help text for this control', 22 | isHorizontal: false 23 | }), 24 | new TextProperty({ 25 | uid: 'variablename', 26 | label : 'Variable name', 27 | defaultValue : 'variable', 28 | helpText : 'The variable associated with this control', 29 | isRequired: true, 30 | isHorizontal: false 31 | }), 32 | // options: 33 | new MultiOptionProperty({ 34 | uid: 'options', 35 | label: 'Options', 36 | helpText: 'Enter options, one per line', 37 | value: [{ 38 | label: 'Option 1', 39 | value: '1' 40 | }, { 41 | label: 'Option 2', 42 | value: '2' 43 | }], 44 | isRequired: true, 45 | isHorizontal: false 46 | }) 47 | ] 48 | }); 49 | }, 50 | render: function(options) { 51 | 52 | options = options || {}; 53 | 54 | var template = _.template(tpl); 55 | 56 | return template({ 57 | control: this, 58 | isDesignTime: options.isDesignTime || false 59 | }); 60 | } 61 | 62 | }); 63 | 64 | return CheckboxListControl; 65 | 66 | }); 67 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/dataUpload.js: -------------------------------------------------------------------------------- 1 | define(['pubsub', 2 | 'site/pubSubTable', 3 | 'rcap/js/ui/controls/gridControl', 4 | 'rcap/js/ui/properties/textProperty', 5 | 'rcap/js/ui/properties/stringValueProperty', 6 | 'utils/variableHandler', 7 | 'text!controlTemplates/dataUpload.tpl' 8 | ], function (PubSub, pubSubTable, GridControl, TextProperty, StringValueProperty, variableHandler, tpl) { 9 | 10 | 'use strict'; 11 | 12 | var DataUploadControl = GridControl.extend({ 13 | init: function () { 14 | this._super({ 15 | type: 'dataupload', 16 | controlCategory: 'Dynamic', 17 | label: 'Data Upload', 18 | icon: 'cloud-upload', 19 | initialSize: [2, 2], 20 | controlProperties: [ 21 | new TextProperty({ 22 | uid: 'variablename', 23 | label: 'Variable', 24 | helpText: 'The variable associated with this data upload', 25 | isRequired: true 26 | }), 27 | new TextProperty({ 28 | uid: 'allowedtypes', 29 | label: 'Allowed types', 30 | helpText: 'List of allowed file types, comma-separated, e.g. csv,tsv. Leave blank for "all"', 31 | isRequired: false 32 | }), 33 | new TextProperty({ 34 | uid: 'buttontext', 35 | label: 'Button Text', 36 | defaultValue: '', 37 | helpText: 'The text that appears on the button', 38 | isRequired: true 39 | }), 40 | new StringValueProperty({ 41 | uid: 'path', 42 | label: 'Save path', 43 | defaultValue: '', 44 | helpText: 'The file path under which uploaded files will be saved', 45 | codeHelpText: 'The R function that returns the path under which uploaded files will be saved', 46 | isRequired: true 47 | }) 48 | ] 49 | }); 50 | }, 51 | getVariableData: function () { 52 | 53 | }, 54 | render: function () { 55 | 56 | var template = _.template(tpl); 57 | 58 | return template({ 59 | control: this 60 | }); 61 | 62 | }, 63 | initialiseViewerItems: function () { 64 | $('[data-controltype="dataupload"]').click(function () { 65 | var params = $(this).data(); 66 | params.controlId = this.id; 67 | PubSub.publish(pubSubTable.showDataUploadDialog, params); 68 | }); 69 | } 70 | }); 71 | 72 | return DataUploadControl; 73 | 74 | 75 | }); 76 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/radioButtonGroup.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 2 | 'rcap/js/ui/properties/textProperty', 3 | 'rcap/js/ui/properties/multiOptionProperty', 4 | 'text!rcap/js/ui/controls/child/templates/radioButtonGroup.tpl' 5 | ], function(BaseControl, TextProperty, MultiOptionProperty, tpl) { 6 | 7 | 'use strict'; 8 | 9 | var RadioButtonGroupControl = BaseControl.extend({ 10 | init: function() { 11 | 12 | this._super({ 13 | type: 'radiobuttongroup', 14 | label: 'Radio Button Group', 15 | icon: 'circle-blank', 16 | controlProperties: [ 17 | new TextProperty({ 18 | uid: 'description', 19 | label: 'Description', 20 | defaultValue: 'Description', 21 | helpText: 'Instructions / help text for this control', 22 | isHorizontal: false 23 | }), 24 | new TextProperty({ 25 | uid: 'variablename', 26 | label: 'Variable name', 27 | defaultValue: 'variable', 28 | helpText: 'The variable associated with this control', 29 | isRequired: true, 30 | isHorizontal: false 31 | }), 32 | // options: 33 | new MultiOptionProperty({ 34 | uid: 'options', 35 | label: 'Options', 36 | helpText: 'Enter options, one per line', 37 | value: [{ 38 | label: 'Option 1', 39 | value: '1' 40 | }, { 41 | label: 'Option 2', 42 | value: '2' 43 | }], 44 | isRequired: true, 45 | isHorizontal: false 46 | }) 47 | ] 48 | }); 49 | }, 50 | render: function(options) { 51 | 52 | options = options || {}; 53 | 54 | var template = _.template(tpl); 55 | 56 | return template({ 57 | control: this, 58 | isDesignTime: options.isDesignTime || false 59 | }); 60 | 61 | } 62 | 63 | }); 64 | 65 | return RadioButtonGroupControl; 66 | 67 | }); -------------------------------------------------------------------------------- /inst/www/js/ui/controls/rPlot.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/gridControl', 2 | 'rcap/js/ui/properties/textProperty', 3 | 'rcap/js/ui/properties/dropdownProperty', 4 | 'rcap/js/ui/properties/autocompleteProperty', 5 | 'text!controlTemplates/rPlot.tpl', 6 | 'text!controlTemplates/rPlot-design.tpl' 7 | ], function(GridControl, TextProperty, DropdownProperty, AutocompleteProperty, tpl, dtpl) { 8 | 9 | 'use strict'; 10 | 11 | var RPlotControl = GridControl.extend({ 12 | init: function() { 13 | this._super({ 14 | type: 'rplot', 15 | controlCategory: 'Dynamic', 16 | label: 'R Plot', 17 | icon: 'signal', 18 | controlProperties: [ 19 | new AutocompleteProperty({ 20 | uid: 'code', 21 | label: 'R Function', 22 | helpText: 'R Function for this control.', 23 | isRequired: true 24 | }), 25 | new TextProperty({ 26 | uid: 'linkUrl', 27 | label : 'Link url', 28 | defaultValue : '', 29 | helpText : 'Link url', 30 | isRequired: false, 31 | }), 32 | new DropdownProperty({ 33 | uid: 'linkTarget', 34 | label: 'Link target', 35 | isRequired: false, 36 | availableOptions: [{ 37 | text: 'Same window', 38 | value: '_self' 39 | }, { 40 | text: 'New window', 41 | value: '_blank' 42 | }], 43 | helpText: 'Where should the link open', 44 | value: '_self' 45 | }) 46 | ] 47 | }); 48 | }, 49 | render: function(options) { 50 | 51 | options = options || {}; 52 | var isDesignTime = options.isDesignTime || false; 53 | 54 | var template = isDesignTime ? _.template(dtpl) : _.template(tpl); 55 | 56 | return template({ 57 | control: this 58 | }); 59 | 60 | }, 61 | initialiseViewerItems: function() { 62 | 63 | } 64 | }); 65 | 66 | return RPlotControl; 67 | 68 | 69 | }); 70 | -------------------------------------------------------------------------------- /R/rcap.progressSpinner.R: -------------------------------------------------------------------------------- 1 | #' @title Write message to ProgressSpinner with specified id 2 | #' 3 | #' @param variableName the name of the variable linked with ProgressSpinner widget control 4 | #' @param message the message that should be displayed 5 | #' @param append should the message be appended to already displayed content 6 | #' @return NULL invisibly 7 | #' @export 8 | rcap.progressSpinner.write <- function(variableName, message, append = FALSE) { 9 | if(haveController()) { 10 | cnt <- get("rcapController", envir = rcapEnv) 11 | control <- Filter(function(x) {x$getVariableName()==variableName}, cnt$getControls()) 12 | if(length(control)!=1) { 13 | stop(paste0("Control for variable '", variableName, "' not found!")); 14 | } 15 | control <- control[[1]] 16 | event <- list("eventType" = "ProgressSpinnerWrite", 17 | "controlId" = control$getId(), 18 | "data" = list("message" = message, 19 | "append" = append) 20 | ) 21 | response <- rcap.events.send(event) 22 | if(response$status != 'Success') { 23 | warning(paste0("Event processing failed ", response$msg)) 24 | } 25 | } else { 26 | warning("Writting to message widget is only available in dashboard view.") 27 | } 28 | invisible(NULL) 29 | } 30 | 31 | #' @title Write message to a collection of ProgressSpinner widgets 32 | #' 33 | #' @param msg named list of messages, with keys being ProgressSpinner widget variableNames 34 | #' @param append single flag or vector of flags controlling if corresponding message should be appended to the widgets content. Last element gets replicated to fit the number of targets. 35 | #' @param ... named parameters, if 'msg' is not defined, these specify progress spinners and messages that should be written. 36 | #' @return NULL invisibly 37 | #' @export 38 | rcap.progressSpinner.msg <- function(msg=NULL, append=FALSE, ...) { 39 | parms <- list(...) 40 | if( is.null(msg) && length(parms) >0 ) { 41 | msg <- parms 42 | } 43 | if( length(msg) > 0 ) { 44 | if(length(append) >= length(msg)) { 45 | appends <- append 46 | } else { 47 | appends <- c(append, rep(append[[length(append)]], length.out = (length(msg) - length(append)))) 48 | } 49 | msg <- as.list(msg) 50 | for(p in 1:length(msg)) { 51 | if(!is.na(names(msg)[[p]])) { 52 | rcloud.rcap:::rcap.progressSpinner.write(names(msg)[[p]], msg[[p]], append = appends[p]) 53 | } 54 | } 55 | } 56 | invisible(NULL) 57 | } 58 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/multiSelect.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 'rcap/js/ui/properties/textProperty', 2 | 'rcap/js/ui/properties/multiOptionProperty', 'text!rcap/js/ui/controls/child/templates/multiSelect.tpl', 'select2/js/select2', 'css!select2/css/select2.min.css'], 3 | function(BaseControl, TextProperty, MultiOptionProperty, tpl) { 4 | 5 | 'use strict'; 6 | 7 | var MultiSelectControl = BaseControl.extend({ 8 | init: function() { 9 | this._super({ 10 | type : 'multiselect', 11 | label : 'Multi-select', 12 | icon: 'indent-right', 13 | controlProperties: [ 14 | new TextProperty({ 15 | uid: 'label', 16 | label : 'Label', 17 | defaultValue : 'Label', 18 | helpText : 'The label for this control', 19 | isHorizontal: false 20 | }), 21 | new TextProperty({ 22 | uid: 'placeholder', 23 | label : 'Placeholder', 24 | defaultValue : 'Please select an option', 25 | helpText : 'The placeholder for this control', 26 | isHorizontal: false 27 | }), 28 | new TextProperty({ 29 | uid: 'variablename', 30 | label : 'Variable name', 31 | defaultValue : 'variable', 32 | helpText : 'The variable associated with this control', 33 | isRequired: true, 34 | isHorizontal: false 35 | }), 36 | // options: 37 | new MultiOptionProperty({ 38 | uid: 'options', 39 | label: 'Options', 40 | helpText: 'Enter options, one per line', 41 | value: [{ 42 | label: 'Option 1', 43 | value: '1' 44 | }, { 45 | label: 'Option 2', 46 | value: '2' 47 | }], 48 | isRequired: true, 49 | isHorizontal: false 50 | }) 51 | ] 52 | }); 53 | }, 54 | render: function(options) { 55 | 56 | options = options || {}; 57 | options.isInFormBuilder = options.isInFormBuilder || false; 58 | 59 | var template = _.template(tpl); 60 | 61 | return template({ 62 | control: this, 63 | // to differentiate it from the control that may already exist on the main design surface: 64 | controlId: options.isInFormBuilder ? this.id + '-formbuilderhosted' : this.id, 65 | isDesignTime: options.isDesignTime || false 66 | }); 67 | 68 | } 69 | }); 70 | 71 | return MultiSelectControl; 72 | 73 | }); -------------------------------------------------------------------------------- /inst/www/styles/vars.scss: -------------------------------------------------------------------------------- 1 | $bg: #fff; 2 | $fg: rgb(62, 62, 62); 3 | $defaultFontSize: 12px; 4 | $bgColor: #F2F2F2; 5 | $defaultFont: 'Open Sans',sans-serif; 6 | 7 | $mainNavBackgroundColor: #222; 8 | $mainNavBackgroundColorHover: lighten($mainNavBackgroundColor, 5%); 9 | $mainNavCurrentPageHover: #070707; 10 | $mainNavColor: #efefef; 11 | $noItemsColor: lighten($mainNavBackgroundColor, 30%); 12 | 13 | $toolbarButtonColorHover: #fff; 14 | $toolbarButtonColor: darken($toolbarButtonColorHover, 15%); 15 | 16 | $messageInformationColor: #3c763d; 17 | $messageWarningColor: #ddc033; 18 | $messageErrorColor: #a94442; 19 | 20 | $topLevelButtonColor: #c37e00; 21 | 22 | /* https://vt.vtp-media.com/ecp/documents/product_Product/521/Logos/4546/att_gdl_glance.pdf */ 23 | /* 24 | $attNewOrange: #ff7200; 25 | $attOrangeHighlight: #fcb314; 26 | $attBlue: #067ab4; 27 | $attBlueHightlight: #3aa5dc; 28 | 29 | $attLime: #b6bf00; 30 | $attLimeHighlight: #dbd810; 31 | $attGreen: #6ebb1f; 32 | $attGreenHighlight: #c4d82d; 33 | $attBurgundy: #b30a3c; 34 | $attBurgundyHighlight: #da3872; 35 | $attPurple: #81017e; 36 | $attPurpleHighlight: #b8509e; 37 | $attDarkBlue: #0c2577; 38 | $attDarkBlueHighlight: #020bb3; 39 | */ 40 | 41 | $attBlue: #009fdb; 42 | $attBlack: #000000; 43 | $attWhite: #ffffff; 44 | $attOrange: #ea7400; 45 | $attLightBlue: #71c5e8; 46 | $attDarkBlue: #0568ae; 47 | $attLightGray: #d2d2d2; 48 | $attGray: #959595; 49 | $attDarkGray: #5a5a5a; 50 | $attYellow: #ffb81c; 51 | $attLightGreen: #b5bd00; 52 | $attGreen: #4ca90c; 53 | $attDarkGreen: #007a3e; 54 | $attLightPurple: #caa2dd; 55 | $attPurple: #9063cd; 56 | $attDarkPurple: #702f8a; 57 | 58 | $attFunctionalLinkBlue: #0568ae; 59 | $attFunctionalDigitalTextBlue: #191919; 60 | $attFunctionalBackgroundGray: #f2f2f2; 61 | $attFunctionalRed: #cf2a2a; 62 | $attFunctionalYellow: #ffb81c; 63 | $attFunctionalGreen: #007a3e; -------------------------------------------------------------------------------- /inst/www/partials/dialogs/_formBuilder.htm: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Form settings

    4 |
    5 | Items 6 | Styling 7 |
    8 |
    9 |
    10 |
    11 | 15 | 16 |
    17 | 22 |
    23 | 26 |
    27 |
    Add or select a control to configure its properties.
    28 |
    29 |
    30 |
    31 |
    32 |
    33 |
    34 |
    35 |
    36 |
    37 | 47 | 52 |
    53 | -------------------------------------------------------------------------------- /inst/www/js/pages/page.js: -------------------------------------------------------------------------------- 1 | define(['controls/factories/controlFactory', 2 | 'rcap/js/Class'], function(ControlFactory) { 3 | 4 | 'use strict'; 5 | 6 | var controlFactory = new ControlFactory(); 7 | 8 | var generateId = function() { 9 | return 'rcap' + Math.random().toString(16).slice(2); 10 | }; 11 | 12 | var Page = Class.extend({ 13 | init: function(options) { 14 | options = options || {}; 15 | 16 | this.navigationTitle = options.navigationTitle || 'New Page'; 17 | this.isEnabled = true; 18 | this.depth = 1; 19 | this.parentId = options.parentId; 20 | this.id = options.id || generateId(); 21 | 22 | this.controls = options.controls || []; 23 | }, 24 | deserialize: function() { 25 | 26 | }, 27 | serialize: function() { 28 | 29 | }, 30 | render: function() { 31 | 32 | }, 33 | getDialogMarkup: function() { 34 | 35 | }, 36 | getDialogValue: function() { 37 | 38 | }, 39 | toJSON: function() { 40 | return { 41 | 'depth': this.depth, 42 | 'id': this.id, 43 | 'parentId' : this.parentId, 44 | 'navigationTitle': this.navigationTitle, 45 | 'isEnabled': this.isEnabled, 46 | 'controls': this.controls, 47 | 'pages': this.pages 48 | }; 49 | }, 50 | getControlByID: function(controlID) { 51 | return _.findWhere(this.controls, { 52 | id: controlID 53 | }); 54 | }, 55 | duplicate: function() { 56 | var dupe = $.extend(true, {}, this); 57 | dupe.id = generateId(); 58 | dupe.controls = []; 59 | 60 | _.each(this.controls, function(c) { 61 | /* 62 | var currentControl = $.extend(true, {}, c); 63 | currentControl.id = generateId(); 64 | dupe.controls.push(currentControl); 65 | */ 66 | 67 | /* 68 | var control = controlFactory.getByKey(c.type); 69 | control.isOnGrid = true; 70 | dupe.controls.push(control); 71 | console.info('pushing control: ', control); 72 | */ 73 | 74 | dupe.controls.push(controlFactory.duplicateControl(c)); 75 | }); 76 | 77 | return dupe; 78 | }, 79 | canAddChild: function() { 80 | return this.depth <= 2; 81 | } 82 | }); 83 | 84 | return Page; 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /inst/www/js/ui/properties/stringValueProperty.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/properties/baseProperty', 'text!templates/stringValueControl.tpl'], function(BaseProperty, tpl) { 2 | 3 | 'use strict'; 4 | 5 | // var getValueType = function(value) { 6 | // if( typeof(value) === 'string') { 7 | // return 'code'; 8 | // } else if (Object.prototype.toString.call(value) === '[object String]') { 9 | // return 'manual'; 10 | // } else { 11 | // // throw new TypeError('was expecting a string or an array'); 12 | // return 'shane'; 13 | // } 14 | // }; 15 | 16 | var StringValueProperty = BaseProperty.extend({ 17 | init: function(options) { 18 | options = options || {}; 19 | this._super({ 20 | type: 'stringvalue', 21 | label: options.label || '', 22 | helpText: options.helpText || '', 23 | defaultValue: options.defaultValue || '', 24 | isRequired: options.isRequired || false, 25 | uid: options.uid, 26 | value: options.value 27 | }); 28 | 29 | this.valueType = options.valueType || 'manual'; 30 | this.codeHelpText = options.codeHelpText || 'Enter a function that retrieves the value at runtime'; 31 | this.serviceName = options.serviceName || 'getRFunctions'; 32 | 33 | }, 34 | render: function(childIndex) { 35 | 36 | var template = _.template(tpl); 37 | 38 | return template({ 39 | property: this, 40 | childIndex: childIndex 41 | }); 42 | 43 | }, 44 | toJSON: function() { 45 | var json = this._super(); 46 | json.valueType = this.valueType; // getValueType(this.value); 47 | return json; 48 | }, 49 | getDialogValue: function() { 50 | if ($('#valueType-manual-' + this.id).is(':checked')) { 51 | 52 | this.valueType = 'manual'; 53 | 54 | // return an array: 55 | return $('#input-valueType-manual-' + this.id).val(); 56 | } else { 57 | 58 | this.valueType = 'code'; 59 | 60 | // return a string: 61 | return $('#input-valueType-code-' + this.id).val(); 62 | } 63 | }, 64 | translateValueToText: function() { 65 | return _.pluck(this.value, 'label').join('\n'); 66 | }, 67 | finalise : function() { 68 | this.optionType = this.valueType; // getValueType(this.value); 69 | } 70 | }); 71 | 72 | return StringValueProperty; 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /inst/www/styles/mixins.scss: -------------------------------------------------------------------------------- 1 | /// Stroke font-character 2 | /// @param {Integer} $stroke - Stroke width 3 | /// @param {Color} $color - Stroke color 4 | /// @return {List} - text-shadow list 5 | @function stroke($stroke, $color) { 6 | $shadow: (); 7 | $from: $stroke*-1; 8 | @for $i from $from through $stroke { 9 | @for $j from $from through $stroke { 10 | $shadow: append($shadow, $i*1px $j*1px 0 $color, comma); 11 | } 12 | } 13 | @return $shadow; 14 | } 15 | 16 | /// Stroke font-character 17 | /// @param {Integer} $stroke - Stroke width 18 | /// @param {Color} $color - Stroke color 19 | /// @return {Style} - text-shadow 20 | @mixin stroke($stroke, $color) { 21 | text-shadow: stroke($stroke, $color)!important; 22 | } 23 | 24 | @mixin noselect() { 25 | -webkit-touch-callout: none; 26 | -webkit-user-select: none; 27 | -khtml-user-select: none; 28 | -moz-user-select: none; 29 | -ms-user-select: none; 30 | user-select: none; 31 | } 32 | 33 | @mixin menuseparator() { 34 | padding-top: 21px; 35 | border-top: 1px solid rgba(255, 255, 255, 0.1); 36 | } 37 | 38 | @mixin cover() { 39 | margin: auto; 40 | position: absolute; 41 | left: 0; 42 | right: 0; 43 | top: 0; 44 | bottom: 0; 45 | } 46 | 47 | @mixin background-opacity($color, $opacity: 0.3) { 48 | background: $color; /* The Fallback */ 49 | background: rgba($color, $opacity); 50 | } 51 | 52 | $gridstack-columns: 24 !default; 53 | 54 | @mixin grid-stack-items($gridstack-columns) { 55 | 56 | .grid-stack.grid-stack { 57 | 58 | > .grid-stack-item { 59 | min-width: 100% / $gridstack-columns; 60 | 61 | @for $i from 1 through $gridstack-columns { 62 | &[data-gs-width='#{$i}'] { width: (100% / $gridstack-columns) * $i; } 63 | &[data-gs-x='#{$i}'] { left: (100% / $gridstack-columns) * $i; } 64 | &[data-gs-min-width='#{$i}'] { min-width: (100% / $gridstack-columns) * $i; } 65 | &[data-gs-max-width='#{$i}'] { max-width: (100% / $gridstack-columns) * $i; } 66 | } 67 | } 68 | } 69 | } 70 | 71 | @mixin fontFace($family,$src,$style: normal,$weight: normal) { 72 | @font-face { 73 | font-family: $family; 74 | src: url('#{$src}.eot'); // IE9 compat 75 | src: url('#{$src}.eot?#iefix') format('embedded-opentype'), // IE8 and below 76 | url('#{$src}.woff') format('woff'), // standards 77 | url('#{$src}.ttf') format('truetype'), // Safari, Android, iOS 78 | url('#{$src}.svg##{$family}') format('svg'); // legacy iOS 79 | 80 | font-style: $style; 81 | font-weight: $weight; 82 | } 83 | } -------------------------------------------------------------------------------- /inst/www/js/ui/controls/dataDownload.js: -------------------------------------------------------------------------------- 1 | define(['pubsub', 2 | 'site/pubSubTable', 3 | 'rcap/js/ui/controls/gridControl', 4 | 'rcap/js/ui/properties/textProperty', 5 | 'rcap/js/ui/properties/stringValueProperty', 6 | 'utils/variableHandler', 7 | 'text!controlTemplates/dataDownload.tpl' 8 | ], function (PubSub, pubSubTable, GridControl, TextProperty, StringValueProperty, variableHandler, tpl) { 9 | 10 | 'use strict'; 11 | 12 | var DataUploadControl = GridControl.extend({ 13 | init: function () { 14 | this._super({ 15 | type: 'datadownload', 16 | controlCategory: 'Dynamic', 17 | label: 'Data Download', 18 | icon: 'cloud-download', 19 | initialSize: [2, 2], 20 | controlProperties: [ 21 | new TextProperty({ 22 | uid: 'variablename', 23 | label: 'Variable', 24 | helpText: 'The variable associated with this data download', 25 | isRequired: true 26 | }), 27 | /*new TextProperty({ 28 | uid: 'allowedtypes', 29 | label: 'Allowed types', 30 | helpText: 'List of allowed file types, comma-separated, e.g. csv,tsv. Leave blank for "all"', 31 | isRequired: false 32 | }),*/ 33 | new TextProperty({ 34 | uid: 'buttontext', 35 | label: 'Button Text', 36 | defaultValue: '', 37 | helpText: 'The text that appears on the button', 38 | isRequired: true 39 | }), 40 | new StringValueProperty({ 41 | uid: 'path', 42 | label: 'Save path', 43 | defaultValue: '', 44 | helpText: 'The file path under which uploaded files will be saved', 45 | codeHelpText: 'The R function that returns the path from which files will be served', 46 | isRequired: true 47 | }) 48 | ] 49 | }); 50 | }, 51 | getVariableData: function () { 52 | 53 | }, 54 | render: function () { 55 | 56 | var template = _.template(tpl); 57 | 58 | return template({ 59 | control: this 60 | }); 61 | 62 | }, 63 | initialiseViewerItems: function () { 64 | $('[data-controltype="datadownload"]').click(function () { 65 | var controlId = this.id; 66 | window.RCAP.listFiles(controlId).then(function(response) { 67 | if(response.status.toLowerCase() === 'success') { 68 | PubSub.publish(pubSubTable.showDataDownloadDialog, { 69 | files: response.data, 70 | id: controlId 71 | }); 72 | } 73 | //TODO display error so it is not just printed on the console 74 | }); 75 | }); 76 | } 77 | }); 78 | 79 | return DataUploadControl; 80 | 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/child/dateRange.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/baseControl', 'rcap/js/ui/properties/textProperty', 2 | 'rcap/js/ui/properties/dropdownProperty', 3 | 'text!rcap/js/ui/controls/child/templates/dateRange.tpl'], function(BaseControl, TextProperty, DropdownProperty, tpl) { 4 | 5 | 'use strict'; 6 | 7 | var DatePickerControl = BaseControl.extend({ 8 | init: function() { 9 | this._super({ 10 | type : 'daterange', 11 | label : 'Date Range', 12 | icon: 'tags', 13 | controlProperties: [ 14 | new TextProperty({ 15 | uid: 'label', 16 | label : 'Label', 17 | defaultValue : 'Label', 18 | helpText : 'The label for this range control', 19 | isHorizontal: false 20 | }), 21 | new DropdownProperty({ 22 | uid: 'intervalType', 23 | label: 'Use interval', 24 | helpText: 'If an interval type is specified, a start date plus interval will be shown rather than the start/end date', 25 | isRequired: false, 26 | availableOptions: [{ 27 | text: 'Days', 28 | value: 'days' 29 | }, { 30 | text: 'Weeks', 31 | value: 'weeks' 32 | }, { 33 | text: 'Months', 34 | value: 'months' 35 | }, { 36 | text: 'Years', 37 | value: 'years' 38 | }], 39 | isHorizontal: false 40 | }), 41 | new TextProperty({ 42 | uid: 'variablename', 43 | label : 'Start/end date variable name', 44 | defaultValue : 'variable', 45 | helpText : 'The start/end date variable', 46 | isRequired: true, 47 | isHorizontal: false 48 | }) 49 | ] 50 | }); 51 | }, 52 | singularInterval: function() { 53 | var intervalType = this.getPropertyValue('intervalType'); 54 | return intervalType ? intervalType.substring(0, intervalType.length - 1) : ''; 55 | }, 56 | render: function() { 57 | var template = _.template(tpl); 58 | 59 | return template({ 60 | control: this 61 | }); 62 | } 63 | }); 64 | 65 | return DatePickerControl; 66 | 67 | }); 68 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | #' Evaluate rhs if lhs is NULL 3 | #' 4 | #' @param l Left hand side, evaluate this first. 5 | #' @param r Right had side, only evaluate it if \code{l} is \code{NULL}. 6 | #' @return \code{l} if it is not \code{NULL}, otherwise \code{r}. 7 | #' @name or 8 | 9 | `%||%` <- function(l, r) { 10 | if (is.null(l)) r else l 11 | } 12 | 13 | #' The environment the notebook and mini.html are run in 14 | #' 15 | #' This environment is used to search for functions that are used 16 | #' to update the dashboard, and for variables that are associated 17 | #' with dashboard controls. 18 | #' 19 | #' This is currently the global environment. 20 | #' 21 | #' @return An environment. 22 | 23 | rcloudEnv <- function() .GlobalEnv 24 | 25 | #' Return relevant session info in a named list 26 | #' 27 | #' This information is returned to the front end 28 | #' @return A list with username and nodename (host) 29 | #' 30 | #' @export 31 | 32 | rcapSessionInfo <- function() { 33 | 34 | list(user=Sys.info()[c("user")], 35 | nodename=toupper(Sys.info()["nodename"])) 36 | 37 | } 38 | 39 | 40 | havePackage <- function(package) { 41 | requireNamespace(package, quietly = TRUE) 42 | } 43 | 44 | 45 | dataFrame <- function(..., stringsAsFactors = FALSE) { 46 | data.frame( 47 | stringsAsFactors = stringsAsFactors, 48 | ... 49 | ) 50 | } 51 | 52 | randomId <- function(prefix = "x") { 53 | rand <- sample(c(letters, 0:9), 8, replace = TRUE) 54 | paste(c(prefix, rand), collapse = "") 55 | } 56 | 57 | #' Return the RCAP version number 58 | #' 59 | #' Check the version number of the loaded package, otherwise check the 60 | #' version installed on the disk. 61 | getRCAPVersion <- function() { 62 | tryCatch( 63 | paste0("v", asNamespace("rcloud.rcap")$`.__NAMESPACE__.`$spec[["version"]]), 64 | error = function(c) { 65 | paste0("v", packageDescription("rcloud.rcap", fields = "Version")) 66 | } 67 | ) 68 | } 69 | 70 | pasteEmpty <- function(...) { 71 | args <- list(...) 72 | argsLens <- vapply(args, length, 1L) 73 | if (any(argsLens == 0)) { 74 | c() 75 | } else { 76 | paste0(...) 77 | } 78 | } 79 | 80 | with_options <- function(new, code) { 81 | old <- set_options(new) 82 | on.exit(set_options(old)) 83 | force(code) 84 | } 85 | 86 | set_options <- function(opts) { 87 | do.call(options, as.list(opts)) 88 | } 89 | 90 | rcap.settings.list <- function() { 91 | ls(envir = rcloud.rcap.settings) 92 | } 93 | 94 | rcap.settings.set <- function(name, value) { 95 | assign(name, value, envir = rcloud.rcap.settings) 96 | } 97 | 98 | rcap.settings.is_set <- function(name) { 99 | exists(name, where = rcloud.rcap.settings) 100 | } 101 | 102 | rcap.settings.get <- function(name) { 103 | get(name, envir = rcloud.rcap.settings) 104 | } -------------------------------------------------------------------------------- /inst/www/js/Class.js: -------------------------------------------------------------------------------- 1 | /* jshint ignore:start */ 2 | define([], function() { 3 | 4 | /* Simple JavaScript Inheritance for ES 5.1 5 | * based on http://ejohn.org/blog/simple-javascript-inheritance/ 6 | * (inspired by base2 and Prototype) 7 | * MIT Licensed. 8 | */ 9 | (function(global) { 10 | "use strict"; 11 | var fnTest = /xyz/.test(function() { 12 | xyz; 13 | }) ? /\b_super\b/ : /.*/; 14 | 15 | // The base Class implementation (does nothing) 16 | function BaseClass() {} 17 | 18 | // Create a new Class that inherits from this class 19 | BaseClass.extend = function(props) { 20 | var _super = this.prototype; 21 | 22 | // Set up the prototype to inherit from the base class 23 | // (but without running the init constructor) 24 | var proto = Object.create(_super); 25 | 26 | // Copy the properties over onto the new prototype 27 | for (var name in props) { 28 | // Check if we're overwriting an existing function 29 | proto[name] = typeof props[name] === "function" && 30 | typeof _super[name] == "function" && fnTest.test(props[name]) ? (function(name, fn) { 31 | return function() { 32 | var tmp = this._super; 33 | 34 | // Add a new ._super() method that is the same method 35 | // but on the super-class 36 | this._super = _super[name]; 37 | 38 | // The method only need to be bound temporarily, so we 39 | // remove it when we're done executing 40 | var ret = fn.apply(this, arguments); 41 | this._super = tmp; 42 | 43 | return ret; 44 | }; 45 | })(name, props[name]) : props[name]; 46 | } 47 | 48 | // The new constructor 49 | var newClass = typeof proto.init === "function" ? proto.hasOwnProperty("init") ? proto.init // All construction is actually done in the init method 50 | : function SubClass() { 51 | _super.init.apply(this, arguments); 52 | } : function EmptyClass() {}; 53 | 54 | // Populate our constructed prototype object 55 | newClass.prototype = proto; 56 | 57 | // Enforce the constructor to be what we expect 58 | proto.constructor = newClass; 59 | 60 | // And make this class extendable 61 | newClass.extend = BaseClass.extend; 62 | 63 | return newClass; 64 | }; 65 | 66 | // export 67 | global.Class = BaseClass; 68 | })(this); 69 | 70 | }); 71 | /* jshint ignore:end */ 72 | -------------------------------------------------------------------------------- /inst/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | 'use strict'; 4 | 5 | var appConfig = { 6 | appPath: require('./bower.json').appPath || '.', 7 | }; 8 | 9 | grunt.initConfig({ 10 | pkg: grunt.file.readJSON('package.json'), 11 | appConfig: appConfig, 12 | sass: { 13 | all: { 14 | options: { 15 | 16 | }, 17 | files: { 18 | 'www/styles/default.css': 'www/styles/default.scss' 19 | }, 20 | trace: true 21 | } 22 | }, 23 | jshint: { 24 | options: { 25 | jshintrc: '.jshintrc', 26 | reporter: require('jshint-stylish') 27 | }, 28 | all: { 29 | src: [ 30 | 'Gruntfile.js', 31 | '<%= appConfig.appPath %>/javascript/**/*.js', 32 | '<%= appConfig.appPath %>/www/**/*.js', 33 | '!<%= appConfig.appPath %>/www/js/vendor/**/*.js', 34 | '!<%= appConfig.appPath %>/www/bower_components/**/*.js', 35 | '!<%= appConfig.appPath %>/www/vendor/**/*.js' 36 | ] 37 | } 38 | }, 39 | watch: { 40 | css: { 41 | files: '**/*.scss', 42 | tasks: ['sass'] 43 | }, 44 | js: { 45 | files: [ 46 | '<%= appConfig.app %>/javascript/**/*.js', 47 | '<%= appConfig.app %>/www/**/*.js', 48 | '!<%= appConfig.appPath %>/www/js/vendor/**/*.js', 49 | '!<%= appConfig.appPath %>/www/bower_components/**/*.js', 50 | '!<%= appConfig.appPath %>/www/vendor/**/*.js' 51 | ], 52 | tasks: ['newer:jshint:all'] 53 | } 54 | }, 55 | bower: { 56 | dev: { 57 | base: 'bower_components', 58 | dest: 'www/vendor', 59 | options: { 60 | checkExistence: true, 61 | debugging: false, 62 | paths: { 63 | bowerDirectory: 'bower_components', 64 | bowerrc: '.bowerrc', 65 | bowerJson: 'bower.json' 66 | } 67 | } 68 | } 69 | } 70 | }); 71 | 72 | grunt.loadNpmTasks('grunt-contrib-copy'); 73 | grunt.loadNpmTasks('grunt-sass'); 74 | grunt.loadNpmTasks('grunt-contrib-watch'); 75 | grunt.loadNpmTasks('grunt-contrib-jshint'); 76 | grunt.loadNpmTasks('grunt-newer'); 77 | grunt.loadNpmTasks('main-bower-files'); 78 | 79 | require('time-grunt')(grunt); 80 | 81 | // dev 82 | grunt.registerTask('default', ['newer:jshint', 'sass', 'bower:dev']); 83 | 84 | }; 85 | -------------------------------------------------------------------------------- /inst/www/js/ui/controls/image.js: -------------------------------------------------------------------------------- 1 | define(['rcap/js/ui/controls/gridControl', 'rcap/js/ui/properties/textProperty', 'rcap/js/ui/properties/dropdownProperty', 2 | 'text!controlTemplates/image.tpl'], 3 | function(GridControl, TextProperty, DropdownProperty, tpl) { 4 | 5 | 'use strict'; 6 | 7 | var ImageControl = GridControl.extend({ 8 | init: function() { 9 | this._super({ 10 | type : 'image', 11 | controlCategory: 'HTML', 12 | label : 'Image', 13 | icon: 'picture', 14 | controlProperties: [ 15 | new TextProperty({ 16 | uid: 'imagesource', 17 | label : 'Image source', 18 | defaultValue : '', 19 | helpText : 'The source of the image', 20 | isRequired: true 21 | }), 22 | new DropdownProperty({ 23 | uid: 'imageLayout', 24 | label: 'Image style', 25 | helpText: 'Whether the image should be tiled, stretched or placed as is.', 26 | isRequired: true, 27 | availableOptions: [{ 28 | text: 'Initial size', 29 | value: 'background-repeat:no-repeat;' 30 | }, { 31 | text: 'Tiled along x axis', 32 | value: 'background-repeat: repeat-x;' 33 | }, { 34 | text: 'Tiled along y axis', 35 | value: 'background-repeat: repeat-y;' 36 | }, { 37 | text: 'Tiled', 38 | value: 'background-repeat: repeat;' 39 | }, { 40 | text: 'Cover', 41 | value: 'background-size: cover;' 42 | }, { 43 | text: 'Stretch', 44 | value: 'background-size: 100% 100%' 45 | }], 46 | defaultValue: 'background-repeat:no-repeat;' 47 | }) 48 | ] 49 | }); 50 | }, 51 | render: function() { 52 | 53 | var template = _.template(tpl); 54 | 55 | return template({ 56 | control: this 57 | }); 58 | }, 59 | initialiseViewerItems : function() { 60 | 61 | $('.grid-stack-item-content.rcap-controltype-image').click(function() { 62 | $('#rcap-stretcher .js-rcap-dynamic').append($('').attr('src', $(this).find('div').attr('data-imgsrc'))); 63 | $('body').addClass('rcap-stretched'); 64 | $('#rcap-stretcher').show(); 65 | 66 | $('#rcap-stretcher img').resizable({ aspectRatio: true, maxHeight: $('#rcap-stretcher .js-rcap-dynamic').height() }); 67 | }); 68 | 69 | $('#rcap-stretcher .stretcher-close').click(function() { 70 | $('#rcap-stretcher .js-rcap-dynamic').empty(); 71 | $('body').removeClass('rcap-stretched'); 72 | $('#rcap-stretcher').hide(); 73 | }); 74 | 75 | } 76 | }); 77 | 78 | return ImageControl; 79 | 80 | }); --------------------------------------------------------------------------------