Searchable Online Catalogues of Knowledge (SOCKs)

The codebase that produced FishSounds is being expanded into an open-source cyberinfrastructure named Searchable Online Catalogues of Knowledge, or SOCKs. It will allow researchers and organizations to share information through user-friendly search forms and result pages. As part of the development process, numerous additional websites similar to FishSounds will be produced and released over the next few years.

Watch our short introduction video to learn more!

If you would like to participate in the project, contact us at FishSoundsContact@gmail.com

The full documentation for SOCKs is still being completed, but to show the flexibility and ease of use of the platform a selection of design elements have been included in the tabs below. There are demonstrations of some of the interface and form options that the SOCKs platform offers, alongside the code required to make them.

Interface Design Elements

The SOCKs platform offers numerous useful options for designing a page layout, organizing search results, and displaying data in easy-to-read ways. The following tabs contain examples of these interface design elements alongside the JSON code required to create them.

There are several convenient functions available in SOCKs to setup a page and create spaces for information to be shown or hidden as a user requires.

Page Framing

Every page in a SOCK is framed by header and footer elements that contain information that describes the website itself. The header provides space for a logo, site title, and site navigation links. The footer is optional and contains space for up to three elements. On FishSounds this space has been used for funder information, a link to citation instructions, and the site's copyright information. On large screens, the header and footer will cling to the top and bottom of the window and the space in between will be filled with the page content, which scrolls if required. On small screens, the header collapses while the footer moves to the bottom of the page content and will only be seen if a user scrolls to the end. The page frames are built automatically when a new page is added, so there is no need to call any code for them.

Expandable Sidebar

A sidebar provides space for information and functionality that users can view or hide as they prefer. On FishSounds sidebars are used to hold the options on search forms. On larger screens the sidebar expands from left to right; on smaller screens it moves to the top of its container and expands top to bottom. By default sidebars are open when the page loads, but can be closed instead (as this example shows) in the builder options. The height (and maximum width) of a sidebar on large screens can be set through the builder options, but by default it will fill the height of the page content space.

Main page content

Required Code:


						interfaceTools.buildSideBar({id:"demo",
label:"Sidebar Label",
showOnLoad:false,
height:200,
maxWidth:300,
largeContent:"<p class='center'>HTML content for large sidebar</p>",
smallContent:"<p class='center p-3'>HTML content for small sidebar<br/>(in case different options are needed on mobile)</p>"});
Accordion Display

The stacked tabs of sections holding these demonstrations are called an accordion. They are also used on FishSounds individual fish record pages, to separate the descriptive information, recordings, and reference summaries. They allow users to focus on the information of relevance to them, while hiding the rest, letting more information be kept on a single page without becoming overwhelming.

Required Code:


						
						var accordionTabs = [
							{id:"layoutDemo",
label:"Page Layout Elements",
showOnLoad:false,
content:"(HTML Content)"},
{id:"resultFrameDemo",
label:"Result Page Elements",
showOnLoad:false,
content:"(HTML Content)"},
{id:"dataDisplayDemo",
label:"Data Display Options",
showOnLoad:false,
content:"(HTML Content)"} ];

interfaceTools.buildAccordion({id:"interfaceDemo",tabs:accordionTabs,headingLevel:4});
Pop-Out Screen

Similar to sidebars and accordions, a pop-out screen provides space for information that users can view if desired, but will otherwise remain hidden. Rather than collapsing into a tab, however, the screen disappears entirely when hidden, and needs to be connected to a button or similar trigger on the page that will make them viewable. This means the trigger can take up less space than a collapsed tab and be placed wherever needed. The pop-out screen itself can be made as wide or narrow as needed, but will cover a segment of the normal page when open. This makes them best used for information such as additional details that appear when a user has selected a record of interest, or instructional information that can be closed before continuing. Only a single pop-out screen widget needs to be added to a page, but it can be tied to multiple triggers displaying different information and clicking them will switch between the content as needed. Clicking the same trigger twice will close the pop-out. On FishSounds, pop-out screens are used to hold quotes from references on some individual reference record pages.

Required Code:


						interfaceTools.buildPopoutScreen();

displayPopoutScreen("popoutDemo1","First Popout Title","Content for the pop-out screen. The screens have a maximum width that is set when they are triggered (the default is 300px), but will only be as wide as needed to fit their content.")
displayPopoutScreen("popoutDemo2","Second Popout Title","Different content displayed in the same pop-out screen container with a declared width of 600px.","600px")
displayPopoutScreen("popoutDemo3","Third Popout Title","Still set to max 600px, but small content needs less space.","600px")
Pop-up Box

Pop-up boxes are much smaller than the screens described above, but are similarly invisible and connected to a button or other trigger on the page. The pop-up will appear immediately next to its trigger in the declared direction from it (defaulting to after the trigger). Only one pop-up box can be open at a time, and opening another will close the first.

This is the default popup box.
They try to fit the dimensions of their container.
This one appears above its trigger and uses a declared icon from the Font Awesome library.
This one appears before its button trigger. It can also have a declared label, but this is the default.
This one appears below its larger trigger and has maximum height and width values.

Required Code:

interfaceTools.buildPopupBox({"content":"This is the default popup box.<br/>They try to fit the dimensions of their container."});

interfaceTools.buildPopupBox({"label":"fas fa-question-circle",
"placement":"above",
"content":"This one appears above its trigger and uses a declared icon from the <a href='https://fontawesome.com/v5/search'>Font Awesome</a> library."});

interfaceTools.buildPopupBox({"trigger":"button",
"placement":"before",
"content":"This one appears before its button trigger. It can also have a declared label,
but this is the default.",
"triggerSize":"btn-sm"});

interfaceTools.buildPopupBox({"placement":"below",
"maxWidth":"100px",
"maxHeight":"75px",
"content":"This one appears below its larger trigger and has maximum height and width values.",
"triggerSize":"fa-2x"});

Page Overlay

An overlay partially obscures the entire screen and provides information in a space in the middle. They can be used similarly to a pop-out screen, but are more commonly used where user attention needs to be drawn to the new information and/or where user input is needed before processes on the normal page can proceed. On FishSounds, page overlays are used in the Administration Portal to confirm deletions and collect additional details in complex forms. The content of an overlay is defined when it is triggered, meaning the same overlay can be used to show different messages resulting from different triggers.

Required Code:


						interfaceTools.buildOverlay();
displayOverlay("HTML Content goes here");
Page Alert

A page alert adds a color-coded banner at the top of the page in which important information can be conveyed to the user. On FishSounds they are used in the Administration Portal to confirm data was successfully submitted or warn that it was not. Page alerts are built into the header portion of Page Framing described above and only need to be triggered. Often an alert will appear on page load, but can also be connected to buttons or other triggers on a page. Page alerts can be of the types and corresponding colors described on the Bootstrap Alerts page, and will default to Primary if the type is missing or not valid.

Required Code:


						displayAlert("Success Message!","success");
displayAlert("Informational Message.","info");
displayAlert("Danger Message...","danger");

SOCKs offers a few convenient interface options to help users navigate through search results. While the visual elements could be called on any page, the built-in functions presumes they are being used on a SOCKs search page and work with the search functions in data schemas.

Page Selector

A set of arrows and numbered buttons that let users jump between pages of results. The buttons are automatically setup to submit a search form and retrieve matching page of results, but as there is no search form on this page they do not function.

Required Code:


						interfaceTools.buildPagination({resultsPerPage:10,totalResultCount:110,currentPage:1});
interfaceTools.buildPagination({resultsPerPage:15,totalResultCount:214,currentPage:12});
interfaceTools.buildPagination({resultsPerPage:15,totalResultCount:53,currentPage:4});
Result Order Selector

A select input with relevant options for sorting search results numerically or alphabetically, forwards or backwards, on a given data field. It requires a companion hidden input in a search form named 'sort', but are automatically setup to submit the search form and retrieve results in the new sort order (as there is no search form on this page it does not function).

In general an order selector is just an input element with the type "singleSelect" (see Input Elements tab below), but as shown in the example the options use a particular format to describe the sort order. The "code" parameter of each sort option uses the format {metadata field name from schema} {pipe character: |} {1 for low-to-high or A-Z ordering; -1 for high-to-low or Z-A ordering}. To sort on multiple fields, list each in the same format separated by a tilda character: ~. Note that ordering on how recently a record was added to a system uses the special "_id" field. The definitions are title values that will appear if you hover over an option for a moment.

Required Code:


						In search form: {type:"hidden",name:"sort",default:"_id|-1"}

var arrayOfSortOptions = [
{code: "_id|-1", label: "Last Added", definition: "Latest added to the system"},
{code: "_id|1", label: "First Added", definition: "Earliest added to the system"},
{code: "refLong|1", label: "First Author, A-Z", definition: "First author surname, A-Z"},
{code: "refLong|-1", label: "First Author, Z-A", definition: "First author surname, Z-A"},
{code: "year|-1~refLong|1", label: "Recent Year", definition: "Year of publication, newest first"},
{code: "year|1~refLong|1", label: "Oldest Year", definition: "Year of publication, oldest first"}
];

interfaceTools.buildResultOrdering({sortOptions:arrayOfSortOptions,valueOnLoad:"First Author, A-Z"});
Combined Result Bar

A bar for managing search results that includes a count of results, a page selector, and a result order selector. On FishSounds the combined result bar is used on the top and bottom of each of the search pages.

1017 Results Found (34 Pages)

Required Code:


						In search form: {type:"hidden",name:"sort",default:"_id|-1"}

var arrayOfSortOptions = [
{code: "_id|-1", label: "Last Added", definition: "Latest added to the system"},
{code: "_id|1", label: "First Added", definition: "Earliest added to the system"},
{code: "refLong|1", label: "First Author, A-Z", definition: "First author surname, A-Z"},
{code: "refLong|-1", label: "First Author, Z-A", definition: "First author surname, Z-A"},
{code: "year|-1~refLong|1", label: "Recent Year", definition: "Year of publication, newest first"},
{code: "year|1~refLong|1", label: "Oldest Year", definition: "Year of publication, oldest first"}
];

interfaceTools.buildResultFrame({schemaName:"references",resultCount:1017,resultsPerPage:30,currentPage:1,sortOptions:arrayOfSortOptions,valueOnLoad:"Recent Year"});
Result Cards

Part of the implementation of a SOCK involves defining result cards for each searchable data type. These cards allow key information for each record to be shown in the list of results in a consistent manner. Once created, however, these cards can also be used on any page where such a summary view would be useful. On FishSounds, each data type has its own result card as seen on the related search screens, but the recording result cards are also used on the individual fish record pages to provide a list of associated recordings. The example code would retrieve all of the recording records and display each in the defined recording card format.

Required Code:


						var resultsArray = await recordingSchema.search({});
interfaceTools.buildRecordingCards(resultsArray);

SOCKs offers numerous options for keeping information organized for easy comprehension by users. The following sections demonstrate different ways to display arrays of data.

Data in Single Line

Some data is best stored as an array, but best displayed in a sentence or single line. This function turns an array of strings or database records into a single string separated by the specified delimiter (defaulting to a semicolon). If using database records, a key option indicating the metadata field to use is required. Spacing around the delimiter can also be declared.

First Item; Second Item; Third Item

Record 1 Item, Record 2 Item, Record 3 Item

First Item|Second Item|Third Item

Required Code:


						
interfaceTools.buildDataString(["First Item","Second Item","Third Item"]);

var databaseRecords = [
{relevantKey:"Record 1 Item",otherMetadata:"Foo"},
{relevantKey:"Record 2 Item",otherMetadata:"Bar"},
{relevantKey:"Record 3 Item",otherMetadata:"Foo Bar"}
];
interfaceTools.buildDataString(databaseRecords,{key:"relevantKey",delimiter:","});

interfaceTools.buildDataString(["First Item","Second Item","Third Item"],{delimiter:"|",spaceBefore:false,spaceAfter:false});
Data in List

Lists are great for keeping data organized and easy-to-follow for users. The SOCKs platform provides a flexible builder that creates a list in the desired style. It can work with list entries provided as strings, but can also work directly with record objects retrieved from the database as long as the key for the relevant field is provided. The following examples demonstrate some of the combinations of data formats and display options.

This example was built using a simple array of strings as data. No options are required, but here we have provided a label for the list and an added class for the items that center them. The default position for a list label is above and aligned to the left.

Simple List:

Item 1

Item 2

Item 3

This example was built using mock database records as data. The options require a 'key' value that indicates which field from the record to use. The 'bulleted' style creates an un-ordered list; 'numbered' would have created an ordered list. Here the list label position is 'center'. A custom default value has also been provided to be used if the list entry is missing in the dataset (the built-in value of 'Unknown' will be used if a default value is not provided.).

Bulleted List:

  • Item 1
  • Item 2
  • Missing Item!!!
This example provides a JSON object of options for each list item, including a 'listEntry' value as a string. For a 'symbolled' style list, each list entry must specify the Font Awesome code to use for the symbol and may have a description that provides an explanation of the symbol's meaning to users. Here the list label position is 'before' and it has a width specified.

Symbolled List:

Item 1

Item 2

Item 3

This example provides a JSON object of options for each list item, including a 'listEntry' value that is a mock database record. The options require a 'key' value that indicates which field from the record to use. The 'labelled' style uses an 'itemLabel' value to add labels, but they do not need to be present for all items. If a 'destination' value is provided for a list entry then it will be linked to that value.

Labelled List:

First Label
Second Label
Item 2
Third Label
Item 3

Required Code:


						
var data_strings = [
"Item 1",
"Item 2",
"Item 3"
];
interfaceTools.buildDataList(data_strings,{listLabel:"Simple List", addClass:"center"});

var data_databaseRecords = [
{relevantKey:"Item 1",otherMetadata:"Foo"},
{relevantKey:"Item 2",otherMetadata:"Bar"},
{relevantKey:"",otherMetadata:"Foo Bar"}
];
interfaceTools.buildDataList(data_databaseRecords,{key:"relevantKey", style:"bulleted", listLabel:"Bulleted List", listLabelPosition:"center", defaultValue:"Missing Item!!!"});

var data_stringsWithOptions = [
{symbol:"fas fa-check-circle",description:"Item has feature",listEntry:"Item 1"},
{symbol:"fas fa-times-circle",description:"Item is missing feature",listEntry:"Item 2"},
{symbol:"fas fa-check-circle",description:"Item has feature",listEntry:"Item 3"}
];
interfaceTools.buildDataList(data_stringsWithOptions,{style:"symbolled", listLabel:"Symbolled List", listLabelPosition:"before", listLabelSize:"col-7"});

var data_databaseRecordsWithOptions = [
{itemLabel:"First Label",destination:"./socks.js",listEntry:{relevantKey:"Item 1",otherMetadata:"Foo"}},
{itemLabel:"Second Label",listEntry:{relevantKey:"Item 2",otherMetadata:"Bar"}},
{itemLabel:"Third Label",listEntry:{relevantKey:"Item 3",otherMetadata:"Foo Bar"}}
];
interfaceTools.buildDataList(data_databaseRecordsWithOptions,{key:"relevantKey", style:"labelled", listLabel:"Labelled List", itemLabelSize:"col-6"});
Data in Grid

For large lists, you may want to split the data across multiple columns. The grid builder function organizes a dataset evenly into the specified number of columns (defaulting to three). On FishSounds data grids are used to organize lists of staff and supporters on the About Us page.

Simple Grid

Item 1

Item 2

Item 3

Item 4

Item 5

Item 6

Item 7

Item 8

HTML Object Grid

Person 1

Job Title 1

Person 2

Job Title 2

Person 3

Job Title 3

Person 4

Job Title 4

Required Code:


						interfaceTools.buildDataGrid(["Item 1","Item 2","Item 3","Item 4","Item 5","Item 6","Item 7","Item 8"]);

var images = [
'<div class="staffMember p-2 mx-auto my-2"> <p class="col-12 bold center nowrap">Person 1</p> <div class="col-12 center"><img class="demoImage m-auto" src="./public/projects/socks/avatar1.png" /></div> <p class="col-12 center">Job Title 1</p> </div>',
'<div class="staffMember p-2 mx-auto my-2"> <p class="col-12 bold center nowrap">Person 2</p> <div class="col-12 center"><img class="demoImage m-auto" src="./public/projects/socks/avatar2.png" /></div> <p class="col-12 center">Job Title 2</p> </div>',
'<div class="staffMember p-2 mx-auto my-2"> <p class="col-12 bold center nowrap">Person 3</p> <div class="col-12 center"><img class="demoImage m-auto" src="./public/projects/socks/avatar3.png" /></div> <p class="col-12 center">Job Title 3</p> </div>',
'<div class="staffMember p-2 mx-auto my-2"> <p class="col-12 bold center nowrap">Person 4</p> <div class="col-12 center"><img class="demoImage m-auto" src="./public/projects/socks/avatar1.png" /></div> <p class="col-12 center">Job Title 4</p> </div>',
];
interfaceTools.buildDataGrid(images,{columnCount:2,htmlContent:true});
Data in Table

For more complex data, the best option for presentation may be a table. The SOCKs platform provides a builder that organizes a dataset into a table with options for filtering capabilities, adding notes to rows, and style options. Data tables are a key part of the SOCKs Administration Portal, and include an 'edit' option that adds a data management widget to each row.

Example Table

First ColumnSecond ColumnThird Column
Default string value Array
of
Values
Row-spanning value
Right-aligned string value Long
list
that
will
...
Left-aligned string value --- A very long string that will be cut off and ended with an ellipsis to ...

Required Code:


								
var dataTable1Headings = [
{id:"column1",label:"First Column",size:"col-4"},
{id:"column2",label:"Second Column",size:"col-2"},
{id:"column3",label:"Third Column",size:"col-6"}
];
var dataTable1Rows = [
[
{id:"column1",size:"col-4",value:"Default string value"},
{id:"column2",size:"col-2",value:["Array","of","Values"]},
{id:"column3",size:"col-6",value:"Row-spanning value",rowspan:2,verticalAlign:"middle"}
],
[
{id:"column1",size:"col-4",value:"Right-aligned string value",align:"right"},
{id:"column2",size:"col-2",value:["Long","list","that","will","be","abbreviated"]},
{}
],
[
{id:"column1",size:"col-4",value:"Left-aligned string value",align:"left"},
{id:"column2",size:"col-2",value:[]},
{id:"column3",size:"col-6",value:"A very long string that will be cut off and ended with an ellipsis to manage this row's and the table's length"}
]
];
var dataTable1Options = {
id:"dataTable1",
title:"Example Table",
width:"col-12",
cutoffArrayCount:5,
abbreviatedArrayCount:4,
cutoffStringCount:75,
abbreviatedStringCount:70,
}
interfaceTools.buildDataTable(dataTable1Headings,dataTable1Rows,dataTable1Options);

Table with Description, Border, Notes and Search Bars

This is a description that appears between the provided title and the table.

The 'note' option will add a column to the beginning of a table with pop-up boxes displaying notes provided in the 'noteEntries' array. These can be stylized using the options described in the pop-up box demo.

The 'search' option will add search fields to the top of each column that will filter the table to only show rows where the values in that column match the text provided. The 'noSearch' option can be added to a heading to remove searching from that column. The full text or set of values in a field will be searched, even if it has been abbreviated, and the ellipsis highlighted if the match is not in the visible text.

Text SearchingList SearchingNon-Searchable ColumnLong Text Searching
Foo Blue
Red
Green
74 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ultrices ut enim eget interdum. Fusce v...
Note for second item
Bar Red
Yellow
128 Duis arcu ligula, eleifend malesuada aliquam ut, commodo et tellus. Nunc rutrum ex non ligula pulvin...
Foo Bar Blue 15 Nulla suscipit faucibus ex id imperdiet. Interdum et malesuada fames ac ante ipsum primis in faucibu...
Bar Red
Blue
93 No Value
Note for fifth item
Foo Green
Yellow
102 Vivamus ac interdum neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur r...
Bar Blue
Purple
256 No Value
Bar Yellow
Blue
Purple
175 No Value
Foo Bar Orange 12 Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris ac au...

Required Code:


								
var dataTable2Headings = [
{id:"column1",label:"Text Searching",size:"col-2"},
{id:"column2",label:"List Searching",size:"col-2"},
{id:"column3",label:"Non-Searchable Column",size:"col-2",noSearch:true},
{id:"column4",label:"Long Text Searching",size:"col-5"}
];
var dataTable2Rows = [
[
{id:"column1",size:"col-2",value:"Foo"},
{id:"column2",size:"col-2",value:["Blue","Red","Green"]},
{id:"column3",size:"col-2",value:"74"},
{id:"column4",size:"col-5",value:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ultrices ut enim eget interdum. Fusce vehicula molestie justo ac congue. Donec consectetur a mi ut laoreet. Vestibulum vitae posuere dolor, sed facilisis neque. Nam blandit elit ut metus placerat, non pulvinar massa finibus. Maecenas blandit elit sit amet nunc fermentum lobortis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec volutpat, nunc nec molestie ultricies, eros est consequat lacus, sed placerat mi elit non ipsum."}
],
[
{id:"column1",size:"col-2",value:"Bar"},
{id:"column2",size:"col-2",value:["Red","Yellow"]},
{id:"column3",size:"col-2",value:"128"},
{id:"column4",size:"col-5",value:"Duis arcu ligula, eleifend malesuada aliquam ut, commodo et tellus. Nunc rutrum ex non ligula pulvinar, sit amet ornare justo feugiat. Fusce et molestie risus. Ut sed quam risus. Quisque non elit non orci fringilla lacinia. Suspendisse potenti. Vivamus vitae tellus metus. Vestibulum placerat justo at purus bibendum, non aliquam eros commodo."}
],
[
{id:"column1",size:"col-2",value:"Foo Bar"},
{id:"column2",size:"col-2",value:["Blue"]},
{id:"column3",size:"col-2",value:"15"},
{id:"column4",size:"col-5",value:"Nulla suscipit faucibus ex id imperdiet. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque tristique augue ut velit sagittis, et ornare urna pretium. Maecenas id nunc neque. Proin lacus lorem, finibus ac est et, consectetur eleifend nunc. Phasellus quis ipsum non augue lacinia volutpat. Maecenas laoreet ipsum ornare, luctus odio a, vestibulum magna. In hac habitasse platea dictumst."}
],
[
{id:"column1",size:"col-2",value:"Bar"},
{id:"column2",size:"col-2",value:["Red","Blue"]},
{id:"column3",size:"col-2",value:"93"},
{id:"column4",size:"col-5",value:""}
],
[
{id:"column1",size:"col-2",value:"Foo"},
{id:"column2",size:"col-2",value:["Green","Yellow"]},
{id:"column3",size:"col-2",value:"102"},
{id:"column4",size:"col-5",value:"Vivamus ac interdum neque. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut et iaculis metus. Nulla eget vehicula magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque ut nulla et dui congue volutpat. In nec lorem vitae justo ultricies ullamcorper sit amet ut nulla."}
],
[
{id:"column1",size:"col-2",value:"Bar"},
{id:"column2",size:"col-2",value:["Blue","Purple"]},
{id:"column3",size:"col-2",value:"256"},
{id:"column4",size:"col-5",value:""}
],
[
{id:"column1",size:"col-2",value:"Bar"},
{id:"column2",size:"col-2",value:["Yellow","Blue","Purple"]},
{id:"column3",size:"col-2",value:"175"},
{id:"column4",size:"col-5",value:""}
],
[
{id:"column1",size:"col-2",value:"Foo Bar"},
{id:"column2",size:"col-2",value:["Orange"]},
{id:"column3",size:"col-2",value:"12"},
{id:"column4",size:"col-5",value:"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Mauris ac auctor arcu. Maecenas lobortis rutrum sem sit amet efficitur. Sed nec magna rhoncus, sollicitudin sem non, condimentum quam."}
]
];
var dataTable2Options = {
id:"dataTable2",
title:"Table with Description, Border, Notes and Search Bars",
description:"This is a description that appears between the provided title and the table.<br/><br/>The 'note' option will add a column to the beginning of a table with pop-up boxes displaying notes provided in the 'noteEntries' array. These can be stylized using the options described in the pop-up box demo.<br/><br/>The 'search' option will add search fields to the top of each column that will filter the table to only show rows where the values in that column match the text provided. The 'noSearch' option can be added to a heading to remove searching from that column. The full text or set of values in a field will be searched, even if it has been abbreviated, and the ellipsis highlighted if the match is not in the visible text.",
width:"col-10 mx-auto mt-4",
height:"300px",
border:true,
missingValuePlaceholder:"No Value",
cutoffStringCount:125,
notes:true,
noteEntries:["","Note for second item","","","Note for fifth item","","",""],
noteOptions:{minWidth:"150px",minHeight:"50px"},
search:true
}
interfaceTools.buildDataTable(dataTable2Headings,dataTable2Rows,dataTable2Options);

Editable Table

The 'edit' option and 'editLinks' values will connect the table to longer data previews, forms that add or update data, sub-tables of additional data, or routes to delete and restore data. (The links listed here will just reload this page). Note that the edit bar takes up col-2 worth of space on the table, and the sizes of other columns must be reduced to accomodate it.

Foo Blue
Red
Green
74
Bar Red
Yellow
128
---
---
Foo Bar Blue 15

Required Code:


						
var dataTable3Rows = [
[
{id:"column1",size:"col-3",value:"Foo"},
{id:"column2",size:"col-3",value:["Blue","Red","Green"]},
{id:"column3",size:"col-3",value:"74"}
],
[
{id:"column1",size:"col-3",value:"Bar"},
{id:"column2",size:"col-3",value:["Red","Yellow"]},
{id:"column3",size:"col-3",value:"128"}
],
[
{id:"column1",size:"col-3",value:"Foo Bar"},
{id:"column2",size:"col-3",value:["Blue"]},
{id:"column3",size:"col-3",value:"15"}
]
];
var dataTable3Options = {
id:"dataTable3",
title:"Editable Table",
description:"The 'edit' option and 'editLinks' values will connect the table to longer data previews, forms that add or update data, sub-tables of additional data, or routes to delete and restore data. (The links listed here will just reload this page). Note that the edit bar takes up col-2 worth of space on the table, and the sizes of other columns must be reduced to accomodate it.",
width:"col-8 mx-auto mt-4",
border:true,
edit:true,
editLinks:{
add:"./socks.js",
view:["displayOverlay('Preview for Row 1')","displayOverlay('Preview for Row 2')","displayOverlay('Preview for Row 3')"],
edit:["./socks.js","./socks.js","./socks.js"],
enrich:["./socks.js","","./socks.js"],
delete:["./socks.js","./socks.js",""],
reactivate:["","","./socks.js"]
}
}
interfaceTools.buildDataTable([],dataTable3Rows,dataTable3Options);

Form Design Elements

The SOCKs platform also provides a large set of form elements that can be used to construct both search forms and data submission forms. As shown below, simple function calls implement a form with the required input fields supplied as an array of JSON objects. The following tabs contain examples of the available input options alongside the JSON code required to create them. Note that if referencing inputs elsewhere, they will have the ID {provided name}Input.

This is html with tags in it

{"type":"tags",
"value":"<p class='center'>This is html with <span style='color:red;'>tags</span> in it</p>"}
{"type":"label",
"value":"This is an extra label",
"target":"basicText"}
{"type":"hidden",
"name":"hidden",
"value":"preset value",
"default":"what to use if value is null"}
{"type":"text",
"name":"basicText",
"label":"Basic Text Input"}
This is space for a pop-up definition of the input
{"type":"text",
"name":"extraText",
"label":"Text Input with Extras",
"placeholder":"A custom placeholder",
"definition":"This is space for a pop-up definition of the input",
"required":true}
A single preset value can also be entered as a string
{"type":"text",
"name":"extendableText",
"label":"Extendable Text Input",
"value":["This is a pre-declared value",
"This is a second one"],
"extendable":true,
"definition":"A single preset value can also be entered as a string"}
Disabled fields will not be submitted and are best used to display values that were set once but cannot be changed
{"type":"text",
"name":"disabledText",
"label":"Disabled Text Input",
"value":"Value that cannot be changed",
"disabled":true,
"definition":"Disabled fields will not be submitted and are best used to display values that were set once but cannot be changed"}
Click the lock to allow changes to the input.

The field remains enabled and will be submitted even if locked. Best used where changes are possible but unlikely or discouraged
{"type":"lockableText",
"name":"lockableText",
"label":"Lockable Text Input",
"value":"Don't change this by mistake",
"definition":"Click the lock to allow changes to the input.<br/><br/>The field remains enabled and will be submitted even if locked. Best used where changes are possible but unlikely or discouraged"}
https://
{"type":"prepend",
"name":"prepend",
"label":"Text Input with Prepend",
"other":{"prepend":"https://",
"prependWidth":"col-lg-3 col-4"}}
Uses built in rules to verify email format
{"type":"email",
"name":"email",
"label":"Email Input",
"definition":"Uses built in rules to verify email format"}
{"type":"password",
"name":"password",
"label":"Password Input",
"required":true}
{"type":"number",
"name":"number",
"label":"Number Input",
"placeholder":"Enter a number",
"other":{"min":1,
"max":5,
"step":0.1}}
{"type":"textarea",
"name":"textarea",
"label":"Textarea",
"other":{"size":"md"}}
Here is another definition space
{"type":"lockableTextarea",
"name":"lockableTextarea",
"label":"Lockable Textarea",
"placeholder":"Custom placeholder text",
"other":{"size":"lg"},
"definition":"Here is another definition space"}
{"type":"singleSelect",
"name":"singleSelect",
"label":"Select One Option",
"value":"option 2",
"other":{"options":[{"code":"1",
"label":"option 1"},
{"code":"2",
"label":"option 2"},
{"code":"3",
"label":"option 3"}]}}
{"type":"select",
"name":"multipleSelect",
"label":"Select Multiple Options",
"value":["option 1",
"option 2"],
"other":{"options":{"Group 1":[{"code":"1",
"label":"option 1"},
{"code":"2",
"label":"option 2"},
{"code":"3",
"label":"option 3"}],
"Group 2":[{"code":"4",
"label":"option 4"},
{"code":"5",
"label":"option 5"}]}}}
{"type":"orderSelect",
"name":"orderSelect",
"label":"Set Order of Item in List",
"value":2,
"other":[{"code":"1",
"label":"Original First Item"},
{"code":"2",
"label":"Item Being Set"},
{"code":"3",
"label":"Original Third Item"},
{"code":"4",
"label":"Original Fourth Item"},
{"code":"5",
"label":"Original Fifth Item"}]}
{"type":"date",
"name":"date",
"label":"Select a Date",
"placeholder":"Click to Select",
"extendable":true}
{"type":"button",
"name":"button",
"label":"Button Input",
"other":{"size":9}}
Option 1
Option 2
Definition for option 2
Option 3
{"type":"radio",
"name":"radio",
"label":"Stacked Radio Input",
"value":"3",
"other":{"options":[{"value":"1",
"label":"Option 1"},
{"value":"2",
"label":"Option 2",
"definition":"Definition for option 2"},
{"value":"3",
"label":"Option 3"}]}}
Option 1
Definition for option 1
Option 2
Option 3
Option 4
{"type":"radio",
"name":"radio",
"label":"Row Radio Input",
"other":{"size":"6",
"options":[{"value":"1",
"label":"Option 1",
"definition":"Definition for option 1"},
{"value":"2",
"label":"Option 2"},
{"value":"3",
"label":"Option 3"},
{"value":"4",
"label":"Option 4"}]}}
{"type":"slider",
"name":"slider",
"label":"Slider Input",
"other":{"min":10,
"max":100,
"step":10}}
{"type":"range",
"name":"range",
"label":"Range Slider Input",
"value":{"start":-3,
"end":8},
"other":{"min":-10,
"max":10,
"func":"updateRangeInputs('rangeInput')"}}
Off On
Applies a switch-like design to a checkbox input
{"type":"switch",
"name":"switch",
"label":"Switch Input",
"other":{"off":"Off",
"on":"On"},
"definition":"Applies a switch-like design to a checkbox input"}
{"type":"file",
"name":"file",
"label":"File Input",
"placeholder":"Click to select file",
"other":{"showDelete":true}}

Some data is best captured through a table, either for organization or so sets of fields can be duplicated at once. Most of the input types described above can also be added within a table structure. The tables themselves are added as an input type through the form builder library, so that they can be used as part of a larger form.

A Simple Table Inside a Larger Form

JSON Code:

{"type":"table",
"name":"simpleTable",
"label":"Author",
"size":12,
"headings":[{"size":4,
"label":"First Name",
"name":"first",
"required":true},
{"size":4,
"label":"Middle Name",
"name":"middle"},
{"size":4,
"label":"Last Name",
"name":"last",
"required":true}],
"rows":[[{"type":"text",
"name":"first"},
{"type":"text",
"name":"middle"},
{"type":"text",
"name":"last"}]]}
An Extendable Table
Group Members

JSON Code:

{"type":"table",
"name":"extendableTable",
"title":"Group Members",
"label":"",
"size":10,
"extendable":true,
"headings":[{"size":3,
"label":"Name",
"name":"name"},
{"size":2,
"label":"Age",
"name":"age"},
{"size":6,
"label":"Description",
"name":"description"}],
"rows":[[{"type":"text",
"name":"text",
"value":"Fred Flintstone"},
{"type":"singleSelect",
"name":"select",
"value":"35-50",
"other":{"options":["Under 18",
"18-35",
"35-50",
"50-75",
"75+"]}},
{"type":"textarea",
"name":"textarea",
"value":"6ft; dark hair"}],
[{"type":"text",
"name":"text",
"value":"Barney Rubble"},
{"type":"singleSelect",
"name":"select",
"value":"18-35",
"other":{"options":["Under 18",
"18-35",
"35-50",
"50-75",
"75+"]}},
{"type":"textarea",
"name":"textarea",
"value":"5ft 4in; blonde hair"}],
[{"type":"text",
"name":"text"},
{"type":"singleSelect",
"name":"select",
"other":{"options":["Under 18",
"18-35",
"35-50",
"50-75",
"75+"]}},
{"type":"textarea",
"name":"textarea"}]]}
Available Input Elements in Tables
  • HTML tags
  • work in tables
  • too!
{"type":"tags",
"value":"<ul><li>HTML tags</li><li>work in tables</li><li>too!</li></ul>"}
Cell-specific definition space
{"type":"label",
"name":"label",
"value":"Label for Inputs",
"definition":"Cell-specific definition space"}
A subheading that can span columns
(but this one doesn't)
{"type":"subheading",
"value":"A subheading that can span columns<br/>(but this one doesn't)",
"colspan":"1"}
See, this one does! It has a colspan of 2
{"type":"text",
"name":"text",
"placeholder":"Text Input"}
{"type":"lockableText",
"name":"lockableText",
"placeholder":"Lockable Text Input"}
Prepend
{"type":"prepend",
"name":"prepend",
"other":{"prepend":"Prepend",
"prependWidth":"col-4"}}
{"type":"email",
"name":"email",
"placeholder":"Email Input"}
{"type":"password",
"name":"password",
"placeholder":"Password Input"}
{"type":"number",
"name":"number",
"placeholder":"Number Input",
"other":{"min":1,
"max":100,
"step":1}}
{"type":"textarea",
"name":"textarea",
"placeholder":"Textarea Input"}
{"type":"lockableTextarea",
"name":"lockableTextarea",
"placeholder":"Lockable Textarea Input"}
{"type":"singleSelect",
"name":"singleSelect",
"other":{"options":["Single Select Input",
"Additional Option"]}}
{"type":"select",
"name":"select",
"value":"Multiple Select Input",
"other":{"options":["Multiple Select Input",
"Choose Multiple Options",
"At The Same Time"]}}
{"type":"date",
"name":"date",
"placeholder":"Date Input"}
{"type":"button",
"name":"button",
"label":"Button Input",
"other":{"size":"9"}}
Stacked
Radio
Input
{"type":"radio",
"name":"stackedRadio",
"other":{"options":["Stacked",
"Radio",
"Input"]}}
Row
Radio
Input
{"type":"radio",
"name":"rowRadio",
"other":{"size":4,
"options":["Row",
"Radio",
"Input"]}}
{"type":"range",
"name":"range",
"other":{"min":1,
"max":10}}
Switch Input
{"type":"switch",
"name":"switch",
"other":{"off":"Switch",
"on":"Input"}}
{"type":"slider",
"name":"tableSlider",
"other":{"min":1,
"max":10}}
{"type":"file",
"name":"file",
"placeholder":"File Input (Click to Select)"}

The usability of a form can be enhanced by having it respond to a user's actions by suggesting data, highlighting errors, etc. In SOCKs, form input objects can accept an array of functions and triggers to add responses to the fields. Two specialized uses of functions, creating previews and changing the form structure based on the data entered, are explained in the tabs below.

Inputs can use the built-in requestList() function to provide auto-complete lists based on fields in a data schema.

JSON Code:

{"type":"button","name":"functionDemoButton","label":"Click for Alert","other":{"size":5},"functions":[{"trigger":"click","event":"alert('Thank you for clicking the button!')"}]}

{"type":"text","name":"color","label":"Autocompleting Color List","functions":[{"trigger":"keyup","event":"requestList(this,'color',this.value,'socks')"},{"trigger":"focus","event":"closeAllLists()"}],"definition":"Inputs can use the built-in requestList() function to provide auto-complete lists based on fields in a data schema."}

In order to help users understand how the data they are entering will look in the system, it can be helpful to show a preview of fields that will be combined or how images will render. SOCKs provides 'preview' and 'mediaPreview' input types that help display what has been entered into form inputs.

Combined Data Preview

A data preview uses an array of variables listing the relevant other form inputs, the default values to be shown if an input is blank, and the html format for the preview (intended to match how the data will be shown in a displayed record) with "{#}" notation used to identify where the values from the array of variables should be used. A function must be attached to each of the provided variable inputs triggered on change and called {previewInputName}PreviewUpdate();

JSON Code:

{"type":"text","name":"genus","label":"Genus","required":true,"functions":[{"trigger":"change","event":"fullNamePreviewUpdate();"}]}

{"type":"text","name":"species","label":"Species","required":true,"functions":[{"trigger":"change","event":"fullNamePreviewUpdate();"}]}

{"type":"text","name":"common","label":"Common Name","required":true,"functions":[{"trigger":"change","event":"fullNamePreviewUpdate();"}]}

{"type":"preview","name":"fullName","label":"Full Name Preview","variables":["genus","species","common"],"defaults":["<i>{Enter Genus value}</i>","<i>{Enter Species value}</i>","<i>{Enter Common Name value}</i>"],"format":"<div class='m-2'><p class='fullWidth center'>{0} {1} ({2})</p></div>"}

Media Previews

Media previews are used both to confirm that the correct file has been selected and to indicate what it will look or sound like if a style class will be applied. When updating records, the current file can be pre-loaded and then removed or changed as needed.

No Preview Available

JSON Code:

{"type":"file","name":"imageFile1","label":"Image File","other":{"fileTypes":".jpg,.jpeg,.png"},"required":true,"functions":[{"trigger":"change","event":"blankImagePreviewUpdate('imageFile1')"}]}

{"type":"mediaPreview","name":"blankImage","label":"Image Preview"}

{"type":"file","name":"imageFile2","label":"Preloaded Image","value":"avatar1.png","other":{"fileTypes":".jpg,.jpeg,.png"},"functions":[{"trigger":"change","event":"preloadedImagePreviewUpdate('imageFile2')"}]}

{"type":"mediaPreview","name":"preloadedImage","label":"Preloaded Image Preview","mediaType":"image","other":{"styleClass":"teamImage","onloadPreview":"./public/projects/socks/avatar1.png"}}

Select File for Preview

JSON Code:

{"type":"file","name":"audioFile1","label":"Audio File","other":{"fileTypes":".mp3,.wav"},"functions":[{"trigger":"change","event":"blankAudioPreviewUpdate('audioFile1')"}]}

{"type":"mediaPreview","name":"blankAudio","label":"Audio Preview","mediaType":"audio","placeholder":"Select File for Preview"}

{"type":"file","name":"audioFile2","label":"Preloaded Audio","value":"audioDemo.mp3","other":{"fileTypes":".mp3,.wav"},"functions":[{"trigger":"change","event":"preloadedAudioPreviewUpdate('audioFile2')"}]}

{"type":"mediaPreview","name":"preloadedAudio","label":"Audio Preview","mediaType":"audio","other":{"onloadPreview":"./public/projects/socks/audioDemo.mp3"}}

Basic Conditional Form

To capture complex data without overwhelming a user, forms in SOCKs can change in response to the information provided. This uses the special 'conditional' input type, which defines a set of possible responses and the fields to display for each. A function is created for each conditional called {name given to conditional}Trigger() that can be used to recheck the relevant value and adjust the fields if needed. Note that the inputs within a conditional will have the ID {provided input name}{condition value}Input for referencing purposes; this prevents duplicate IDs if the same field name is used in multiple conditions.

Subfields for First Option

This field has the ID 'field1FirstInput', based on its name (field1) and the condition value for which it is shown (First)

Subfields for Second Option

Subfields for Third Option

JSON Code:

{"type":"singleSelect","name":"basicConditional","label":"Select Option","required":true,"other":{"options":["","First","Second","Third"]},"functions":[{"trigger":"change","event":"detailsTrigger($(this).val())"}]}

{"type":"conditional","name":"details","conditionField":"basicConditional","results":{"First":[{"type":"tags","name":"label","value":"<p class='big center'>Subfields for First Option</p>"},{"type":"text","name":"field1","label":"Input A","definition":"This field has the ID 'field1FirstInput', based on its name (field1) and the condition value for which it is shown (First)"},{"type":"text","name":"field2","label":"Input B"},{"type":"text","name":"field3","label":"Input C"}],"Second":[{"type":"tags","name":"label","value":"<p class='big center'>Subfields for Second Option</p>"},{"type":"text","name":"field1","label":"Input D"},{"type":"text","name":"field2","label":"Input E"},{"type":"text","name":"field3","label":"Input F"}],"Third":[{"type":"tags","name":"label","value":"<p class='big center'>Subfields for Third Option</p>"},{"type":"text","name":"field1","label":"Input G"},{"type":"text","name":"field2","label":"Input H"},{"type":"text","name":"field3","label":"Input I"}],"default":""}}

Conditional on Any Value

To display fields conditional on whether there is *any* value present, define the conditional values as undefined and an empty string for the condition if there is no value, and default for the condition where there is a value.

This field has the ID 'field1defaultInput'

JSON Code:

{"type":"text","name":"textConditional","label":"Enter Text to See Additional Fields","functions":[{"trigger":"keyup","event":"additionalFieldsTrigger($(this).val())"}]}

{"type":"conditional","name":"additionalFields","conditionField":"textConditional","results":{"undefined":"","":"","default":[{"type":"text","name":"field1","label":"Additional Input 1","definition":"This field has the ID 'field1defaultInput'"},{"type":"text","name":"field2","label":"Additional Input 2"}]}}

Conditional on Switch Input

If making a conditional on a switch input, the conditional values should be 'true' and 'false' (though default can be used in place of either), and the :checked property should be provided rather than a value, as shown in the code for the following example.

Field Set 1 Field Set 2
This field has the ID 'field1trueInput'

JSON Code:

{"type":"switch","name":"switchConditional","label":"Switch Triggering Conditional Fields","other":{"off":"Field Set 1","on":"Field Set 2"},"functions":[{"trigger":"change","event":"switchConditionsTrigger($(this).is(':checked'))"}]}

{"type":"conditional","name":"switchConditions","conditionField":"switchConditional","results":{"false":[{"type":"text","name":"field1","label":"Input I","definition":"This field has the ID 'field1trueInput'"},{"type":"text","name":"field2","label":"Input II"},{"type":"text","name":"field3","label":"Input III"}],"true":[{"type":"text","name":"field1","label":"Input IV"},{"type":"text","name":"field2","label":"Input V"},{"type":"text","name":"field3","label":"Input VI"}]}}

Conditional on File Input and Nested Conditionals

If making a conditional on a file input, the conditionField should be listed as {provided file input name}InputValueChecker, which is a hidden flag included with all file inputs that keeps track of whether or not a file is attached. The condition values should be listed as 1 for when a file is present, and default for when there is no file. In the future, support for conditions on different file types will be added.

https://

JSON Code:

{"type":"file","name":"filename","label":"Select Image File to See Additional Fields","required":false,"other":{"fileTypes":".jpg,.jpeg,.png"},"functions":[{"trigger":"change","event":"copyrightTrigger($(this).val())"}]}

{"type":"conditional","name":"copyright","conditionField":"filenameInputValueChecker","results":{"1":[{"type":"text","name":"copyrightHolder","label":"Copyright Holder","required":true,"functions":[{"trigger":"change","event":"copyrightPreviewUpdate();copyrightdefaultPreviewUpdate();"}]},{"type":"singleSelect","name":"copyrightLicense","label":"Copyright License","required":true,"other":{"options":["","CC BY-NC 4.0","CC BY 4.0","CC BY-SA 4.0","CC BY-NC-SA 4.0","CC BY-ND 4.0","CC BY-NC-ND 4.0"]},"functions":[{"trigger":"change","event":"copyrightPreviewUpdate();copyrightdefaultPreviewUpdate();"}]},{"type":"text","name":"copyrightSourceName","label":"Image Source Name","required":false,"functions":[{"trigger":"change","event":"copyrightPreviewUpdate();copyrightdefaultPreviewUpdate();"}]},{"type":"prepend","name":"copyrightSourceLink","label":"Image Source Link","other":{"prepend":"https://","prependWidth":"col-lg-4 col-3"},"required":false,"functions":[{"trigger":"change","event":"sourceLink1Trigger($(this).val());copyrightPreviewUpdate();copyrightdefaultPreviewUpdate();"}]},{"type":"conditional","name":"sourceLink","conditionField":"copyrightSourceLink1Input","results":{"":{"type":"preview","name":"copyright","label":"Copyright Preview","variables":["copyrightHolder1","copyrightLicense1","copyrightSourceName1"],"defaults":["<i>{Enter Copyright Holder value}</i>","<i>{Select Copyright License value}</i>","contributed by owner"],"format":"<div class='m-2'><p class='fullWidth center'>{0}, license: {1}, source: {2}</p></div>","loadPreview":true},"default":{"type":"preview","name":"copyright","label":"Copyright Preview","variables":["copyrightHolder1","copyrightLicense1","copyrightSourceName1","copyrightSourceLink1"],"defaults":["<i>{Enter Copyright Holder value}</i>","<i>{Select Copyright License value}</i>","<i>{Enter Source Name value}</i>",""],"format":"<div class='m-2'><p class='fullWidth center'>{0}, license: {1}, source: <a href='https://{3}'>{2}</a></p></div>","loadPreview":true}}}],"default":""}}