<!-- 
/*
Copyright 2017 apHarmony

This file is part of jsHarmony.

jsHarmony is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

jsHarmony is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this package.  If not, see <http://www.gnu.org/licenses/>.
*/
-->
<script type="text/x-tutorial-info">
{
  "ID": "field_links",
  "Title": "Links",
  "Menu": ["Models","Fields / Controls","Built-in Controls"],
  "Code": [
    "/models/FieldLinks_Standard_Form.json",
    "/models/FieldLinks_Standard_Grid.json",
    "/models/FieldLinks_Cust.json",
    "/models/FieldLinks_Parameters_Form.json",
    "/models/FieldLinks_Parameters_Grid.json",
    "/models/FieldLinks_Download_Form.json",
    "/models/FieldLinks_Download_Grid.json",
    "/models/FieldLinks_Select_Form.json",
    "/models/FieldLinks_Select_Grid.json",
    "/models/FieldLinks_Cust_LOV.json",
    "/models/FieldLinks_URL_Form.json",
    "/models/FieldLinks_URL_Grid.json",
    "/models/FieldLinks_JS_Form.json",
    "/models/FieldLinks_JS_Grid.json",
    "/models/FieldLinks_WindowOptions_Form.json",
    "/models/FieldLinks_WindowOptions_Grid.json",
    "/models/FieldLinks_Onclick_Precedence.json"
  ],
  "Demo": [
    { "url": "jsHarmonyTutorials/FieldLinks_Standard_Form?action=update&cust_id=1", "title": "Standard Links - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_Standard_Grid", "title": "Standard Links - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_Parameters_Form?action=update&cust_addr_id=1", "title": "Link Parameters - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_Parameters_Grid", "title": "Link Parameters - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_Download_Form?action=update&cust_id=1", "title": "Download Links - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_Download_Grid", "title": "Download Links - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_Select_Form?action=update&cust_id=1", "title": "Select Links - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_Select_Grid", "title": "Select Links - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_URL_Form?action=update&cust_id=1", "title": "Custom URL Links - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_URL_Grid", "title": "Custom URL Links - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_JS_Form?action=update&cust_id=1", "title": "Custom JS Links - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_JS_Grid", "title": "Custom JS Links - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_WindowOptions_Form?action=update&cust_id=1", "title": "Window Options - Form" },
    { "url": "jsHarmonyTutorials/FieldLinks_WindowOptions_Grid", "title": "Window Options - Grid" },
    { "url": "jsHarmonyTutorials/FieldLinks_Onclick_Precedence", "title": "Onclick Precedence" }
  ]
}
</script>

<h3>Overview / Syntax</h3>
The field.link property provides a way to perform interface actions, or to link controls to other forms and grids.  Links are available for the following controls:
<ul>
  <li>label</li>
  <li>html</li/>
  <li>button</li>
  <li>linkbutton</li>
</ul>

Links have the following actions available:
<ul>
  <li><b>insert</b>: Open a model for Insert</li>
  <li><b>browse</b>: Open a model for Browse</li>
  <li><b>update</b>: Open a model for Update</li>
  <li><b>savenew</b>: Save the current data, and open a model for Insert (Save &amp; New record)</li>
  <li><b>download</b>: Download a file</li>
  <li><b>select</b>: (Only on Popup-LOV Grid) Select the current row</li>
  <li><b>url</b>: Open a URL on click (EJS expressions supported)</li>
  <li><b>js</b>: Run a custom JavaScript expression on click</li>
</ul>

The link syntax is as follows:
<pre>
[i]link[/i] :=
  insert[i](window_options)[/i]:MODELID&amp;[i]parameter1[/i]&amp;[i]parameter2[/i]&amp;[i]parameter3[/i]&amp;...
  browse[i](window_options)[/i]:MODELID&amp;[i]parameter1[/i]&amp;[i]parameter2[/i]&amp;[i]parameter3[/i]&amp;...
  update[i](window_options)[/i]:MODELID&amp;[i]parameter1[/i]&amp;[i]parameter2[/i]&amp;[i]parameter3[/i]&amp;...
  savenew[i](window_options)[/i]:MODELID&amp;[i]parameter1[/i]&amp;[i]parameter2[/i]&amp;[i]parameter3[/i]&amp;...
  download:MODELID&amp;DOWNLOAD_FIELD=KEY_VALUE_FIELD
  select
  url[i](window_options)[/i]:[i]Relative or Absolute URL, with optional &lt;# #&gt; EJS Parameters[/i]
  js:[i]JavaScript Expression[/i]

[i]parameter[/i] :=
  FIELD_NAME
  FIELD_NAME=NAME_OF_FIELD_WITH_VALUE
  FIELD_NAME='CONSTANT'
  FIELD_NAME=NUMBER
  tabs={\"PARENT_MODEL\":\"SELECTED_TAB_MODEL\"}

[i]window_options[/i] := [i]window_option1=value1[/i],[i]window_option2=value2[/i],[i]window_option3=value3[/i]
[i]window_option[/i] :=
  target     //The link frame target, ex. _blank, _self
  width      //The width of the popup window
  height     //The height of the popup window
  resizable  //Whether or not the window is resizable (1 or 0), IE-only
  scrollbars //Whether or not scrollbars are displayed (1 or 0)
  status     //Whether or not status is displayed (1 or 0)
  top        //Screen top offset of the window
  left       //Screen left offset of the window
  Any other window parameters supported by window.open
</pre>

<b>Example Links:</b>
<pre>
update:Cust
update:Cust&cust_id

update:Cust&cust_id=5
update:Cust&cust_id=NEW_cust_id

url(target=_blank,width=800,height=600):https://www.example.com
url:/Cust?action=update&cust_id=&lt;#=data.cust_id#&gt;
js:XExt.Alert('Test Popup');
</pre>

<h4>Form Example</h4>
<%-getScreenshot('jsHarmonyTutorials/FieldLinks_Standard_Form?action=update&cust_id=1&popup=1','Standard Links')%>
<pre>
{ 
  "table":"cust",
  "layout":"form",
  "onecolumn":true,
  "caption": ["Standard Links"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name": "cust_name", "caption": "Customer Name", "control": "textbox"},

    {"control": "button", "value":"Insert", "link": "insert:FieldLinks_Cust"},
    {"control": "button", "value":"Browse", "link": "browse:FieldLinks_Cust"},
    {"control": "button", "value":"Update", "link": "update:FieldLinks_Cust"},
    {"control": "button", "value":"Save & New", "link": "savenew:"},
    {"control": "button", "value":"Download", "link": "download:FieldLinks_Cust&cust_doc"},
    {"control": "button", "value":"URL", "link": "url:/_listing"},
    {"control": "button", "value":"JavaScript", "link": "js:XExt.Alert('JavaScript Command');"}
  ]
}
</pre>

<h3 data-level="2">Insert / Browse / Update / SaveNew</h3>
The insert, browse, update, and savenew links are used to interface with other database-driven models.<br/>
<br/>

<b>Querystring Parameter Passing</b><br/>
Links are generated based on the current record data.  The following parameters will be added to the link's querystring, in the following priority (from lowest to highest):
<ul>
  <li>Any of the current page's querystring parameters</li>
  <li>If the action is "insert" or "savenew", any of the parameters in the current row</li>
  <li>If the action is "update", "browse", or "download", the primary key of the target model, if it is in the current row</li>
  <li>Automatic bindings generated by the system, based on the current model and the target model</li>
  <li>Explicitly defined parameters in the link</li>
</ul>

Parameters are defined by adding them after the target model, separated by "&amp;":
<pre>
[b]No Parameters (Automatic Parameters)[/b]
update:Cust

[b]One Parameter[/b]
update:Cust&cust_id

update:Cust&cust_id=5
update:Cust&cust_id=CUST_ID_ON_PARENT

[b]Multiple Parameters[/b]
update:Cust&cust_id&item_id
update:Cust&cust_id=5&item_id=10
update:Cust&cust_id=PARENT_cust_id&item_id=favorite_item_id
</pre>

Parameters can be explicitly defined in a variety of ways:
<ul>
  <li>TARGET_MODEL_FIELD_NAME=PARENT_MODEL_FIELD_NAME</li>
  <li>TARGET_MODEL_FIELD_NAME [i]Shorthand when TARGET_MODEL_FIELD_NAME is the same as PARENT_MODEL_FIELD_NAME[/i]</li>
  <li>TARGET_MODEL_FIELD_NAME='CONSTANT STRING'</li>
  <li>TARGET_MODEL_FIELD_NAME=NUMBER</li>
</ul>

Additionally, tabs can be set by passing a tabs parameter:
<pre>
tabs={\"PARENT_MODEL\":\"SELECTED_TAB_MODEL\"}
</pre>
Multiple tabs are supported:
tabs={\"PARENT_MODEL1\":\"SELECTED_TAB_MODEL1\", \"PARENT_MODEL2\":\"SELECTED_TAB_MODEL2\"}

The example below contains all of these scenarios:
<%-getScreenshot('jsHarmonyTutorials/FieldLinks_Parameters_Form?action=update&cust_addr_id=1&popup=1','Link Parameters')%>
<pre>
{ 
  "table":"cust_addr",
  "layout":"form",
  "onecolumn":true,
  "caption": ["Link Parameters"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name":"cust_addr_line1","caption":"Address"},
    
    {"control": "button", "value":"Auto Parameters", "link": "update:FieldLinks_Cust"},
    {"control": "button", "value":"Explicit Parameters", "link": "update:FieldLinks_Cust&cust_id"},
    {"control": "button", "value":"Static Parameters - Number", "link": "update:FieldLinks_Cust&cust_id=3"},
    {"control": "button", "value":"Static Parameters - String", "link": "insert:FieldLinks_Cust&cust_name='New customer'"},
    {"control": "button", "value":"Multiple Parameters", "link": "insert:FieldLinks_Cust?cust_name='New customer'&cust_sts='ACTIVE'"},
    {"control": "button", "value":"Field-Bound Parameters", "link": "insert:FieldLinks_Cust&cust_name=cust_addr_line1"},
    {"control": "button", "value":"Tab Selection", "link": "update:FieldLinks_Cust&tabs={\"jsHarmonyTutorials/FieldLinks_Cust\":\"jsHarmonyTutorials/FormPopup_Cust_Tab2\"}"}
  ]
}
</pre>

<h3 data-level="2">Download</h3>
Both forms and grids can provide "Download" links and buttons, so that users can download a target file that has been uploaded.
<%-getScreenshot('jsHarmonyTutorials/FieldLinks_Download_Grid?popup=1','Download Links')%>
<pre>
{ 
  "table":"cust",
  "layout":"grid",
  "onecolumn":true,
  "caption": ["Download Links"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name": "cust_name", "caption": "Customer Name", "control": "textbox"},
    {"control": "button", "value":"Download", "link": "download:FieldLinks_Cust&cust_doc"},
    {"control": "label", "value":"Download", "link": "download:FieldLinks_Cust&cust_doc",
     "type":"file", "controlparams": { "data_folder": "tutorials_cust_doc", "data_file_prefix": "cust_doc" } }
  ]
}
</pre>
In the standard case, with the button, the button will always display, and show a "Download file not found" message if the file is missing.<br/>
<br/>
The label control above uses a more advanced method, so that the link will hide when the download file is not available.  In order to do this, the field.type and file definition needs to be configured.<br/>
</br>
If a label control has a field.type, it is directly linked to a database column or file field.  The label will then be hidden if the database column is blank, or if the file field is empty.<br/>
<br/>
In this example above, the field.controlparams is used to specify the location of the download file.  Normally, the "data_file_prefix" will default to field.name, however here we manually specify it since the file field is defined in another model.<br/>
<br/>
Download links are defined as follows:
<pre>
download:MODELID&DOWNLOAD_FIELD=KEY_VALUE_FIELD
</pre>
The DOWNLOAD_FIELD parameter defines the field on the target model that references the file.  The DOWNLOAD_FIELD must have field.type="file", and have controlparams.data_folder set to the target folder.<br/>
<br/>
The KEY_VALUE_FIELD parameter defines the parent model's field that has the primary key ID that will be requested from the download model.<br/>
<br/>
If KEY_VALUE_FIELD is not defined, it will default to the primary key of the download model, if it is found in the parent model.  For example, if the download model has a primary key "file_id", and the "file_id" field is in the parent model that contains the link field, then "file_id" will be used as the KEY_VALUE_FIELD.

<h3 data-level="2">Select</h3>
Select links can only be used within a Popup List of Values grid.  They provide an alternate way to select a value in a popup List of Values.<br/>
<br/>
Popup list of values are displayed when a user clicks on a magnifying glass next to a field with field.popuplov defined:
<%-getScreenshot('jsHarmonyTutorials/FieldLinks_Select_Form?popup=1&cust_id=1&action=update','Select Links')%>
<%-getScreenshot('jsHarmonyTutorials/FieldLinks_Select_Form?popup=1&cust_id=1&action=update','Select Links - Popup Open', { onload:function(){ return new Promise(function(resolve){
  var $ = jshInstance.$;
  $('.cust_parent_id_xlookup.xlookup').click();
  jshInstance.XExt.waitUntil(function(){ return $('.xelemjsHarmonyTutorials_FieldLinks_Cust_LOV.unnamed_button_control_1').length; }, resolve);
}); } })%>
<pre>
//Form with the Popup List of Values control
{ 
  "table":"cust",
  "layout":"form",
  "onecolumn":true,
  "caption": ["Select Links"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name": "cust_name", "caption": "Customer Name", "control": "textbox"},
    {"name": "cust_parent_id", "caption": "Parent Customer ID", "control": "label","popuplov":{ "target":"FieldLinks_Cust_LOV", "code_val":"cust_id" }}
  ]
}

//Grid with the List of Values contents
"FieldLinks_Cust_LOV": {
  "table":"cust",
  "layout":"grid",
  "actions":"B",
  "caption":["Customer","Customers"],
  "fields":[
    {"name":"cust_id","caption": "ID"},
    {"name":"cust_name","caption":"Name","link":"select"},
    {"control": "button", "value":"Select", "link": "select"}
  ]
}
</pre>

In this example, both the "cust_name" field and the button have a "select" link.  When the user clicks on either of the controls, it will have the same effect as clicking the "Select" link on the left of the grid, selecting the row as the result of the List of Values popup.

<h3 data-level="2">Custom URL</h3>
In some instances, it may be necessary to link to a custom URL.  Custom URLs can contain any of the data in the current model by using EJS.

<%-getScreenshot('jsHarmonyTutorials/FieldLinks_URL_Form?action=update&cust_id=1&popup=1','Custom URL Links')%>
<pre>
{ 
  "table":"cust",
  "layout":"form",
  "onecolumn":true,
  "caption": ["URL Links"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name": "cust_name", "caption": "Customer Name", "control": "textbox"},
    {"name": "cust_email", "control": "hidden"},

    {"control": "button", "value":"Internal URL", "link": "url:/&lt;%=xmodel.namespace%&gt;FieldLinks_Cust?action=update&cust_id=&lt;%=data.cust_id%&gt;"},
    {"control": "button", "value":"External URL", "link": "url:https://www.apharmony.com"},
    {"control": "button", "value":"Email", "link": "url:mailto:&lt;%=data.cust_email%&gt;"}
  ]
}
</pre>

In this example, the Internal URL dynamically adds the current model's namespace to the URL, and the key field.  The mailto link also integrates the current row's email address field.<br/>
<br/>
Any fields used in the EJS expressions must be defined in the fields array.  In this example, the "cust_email" field is used.  Since "cust_email" was not part of the original form, it was included as a hidden field so that it would still be selected from the database.<br/>
<br/>
If the resulting link / EJS expression is blank, no action will be taken by the users browser.<br/>
<br/>
The following EJS variables can be used in a url link EJS expression.  EJS variables are escaped using &lt;# #&gt; tags:
<pre>
All models
----------
data    //The current row's data, ex: data.cust_id

xmodel  //The jsHarmony Model instance
modelid //The jsHarmony Model ID
_GET    //The GET parameters

Grid models additionally have the following parameters
----------
rowid     //The current table row #
datatable //The array of rows currently being rendered

Additionally, the following JavaScript global objects are available
----------
jsh     //The jsHarmony instance
XExt    //The jsHarmony JavaScript Client-side Extensions
_       //Lodash library
moment  //Moment.js library
</pre>

<h3 data-level="2">Custom JavaScript</h3>
Links can be used to execute custom JavaScript expressions.

<%-getScreenshot('jsHarmonyTutorials/FieldLinks_JS_Form?action=update&cust_id=1&popup=1','Custom JS Links')%>
<pre>
{ 
  "table":"cust",
  "layout":"form",
  "onecolumn":true,
  "caption": ["JS Links"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name": "cust_name", "caption": "Customer Name", "control": "textbox"},
    {"name": "cust_email", "control": "hidden"},

    {"control": "button", "value":"JavaScript Alert", "link": "js:XExt.Alert('Sample Alert');"},
    {"control": "button", "value":"JavaScript popupForm", "link": "js:XExt.popupForm(xmodel.namespace+'FieldLinks_Cust','insert',{ 'cust_name': 'New Customer' });"},
    {"control": "button", "value":"JavaScript Email", "link": "js:var email = xmodel.get('cust_email'); if(email){ var url = 'mailto:'+email; jsh.getFileProxy().prop('src', url); }"}
  ]
}
</pre>

The following variables are accessible from the JavaScript expression:
<pre>
jsh       //The jsHarmony instance
XExt      //The jsHarmony JavaScript XExt Client-side Extensions
XForm     //The jsHarmony JavaScript XForm AJAX/CRUD Library
XValidate //The jsHarmony JavaScript XValidate Form Validation Library
XFormat   //The jsHarmony JavaScript XFormat Formatting Library
XPage     //The jsHarmony JavaScript Page controller
_         //Lodash library
$         //jQuery library
moment    //Moment.js library
async     //Async.js library
_GET      //The GET parameters
XModels   //An associative array of the jsHarmony Models currently active on the client browser
xmodel    //The current jsHarmony Model's instance
_this     //The current jsHarmony Model's Class instance (used in Custom Forms / Custom JS)
modelid   //The current jsHarmony Model's ID
</pre>

<h3 data-level="2">Window Options and Popup Windows</h3>
By default, models with the model.popup property will open in a new window, while models without model.popup will open in the same window.  This behavior can be changed by using window options:

<%-getScreenshot('jsHarmonyTutorials/FieldLinks_WindowOptions_Form?action=update&cust_id=1&popup=1','Window Options')%>
<pre>
{ 
  "table":"cust",
  "layout":"form",
  "onecolumn":true,
  "caption": ["Link Window Options"],
  "fields": [
    {"name": "cust_id", "caption": "Customer ID", "control": "label"},
    {"name": "cust_name", "caption": "Customer Name", "control": "textbox"},
    
    {"control": "button", "value":"Link to Popup Form", "link": "update:FieldLinks_Cust"},
    {"control": "button", "value":"Link to Popup Form in Same Window", "link": "update(target=_self):FieldLinks_Cust"},
    {"control": "button", "value":"Regular Link to Same Window", "link": "url:https://www.apharmony.com"},
    {"control": "button", "value":"Regular Link in Popup", "link": "url(target=_blank):https://www.apharmony.com"},
    {"control": "button", "value":"Regular Link in Popup w/Dimensions", "link": "url(target=_blank,width=600,height=600):https://www.apharmony.com"}
  ]
}    
</pre>
Window options are added in parenthesis before the link target.  The window options can be either "target" to set the link window target, or any of the window.open parameters, such as "width" and "height".<br/>
<br/>
Setting "target" to "_self" will open the target model in the same window, while setting "target" to "_blank" will open the target model in a new window.

<h3>Onclick Event Handler Precedence</h3>
In addition to the field.link property, the field.onclick event can be bound for a fully custom event handler.
<%-getScreenshot('jsHarmonyTutorials/FieldLinks_Onclick_Precedence?popup=1','Onclick Precedence')%>
<pre>
{ 
  "layout":"form",
  "unbound":"true",
  "onecolumn":true,
  "caption": ["Onclick Precedence"],
  "fields": [
    {"control": "button", "value":"JavaScript Link", "link": "js:XExt.Alert('Sample JS Link');"},
    {"control": "button", "value":"JavaScript Onclick", "onclick": "js:XExt.Alert('Sample JS Onclick');"},
    {"control": "button", "value":"Button with both JavaScript Link + Onclick", "link": "js:XExt.Alert('Sample JS Link');", "onclick": "js:XExt.Alert('Sample JS Onclick');"},
    {"control": "label", "value":"Label with both JavaScript Link + Onclick", "link": "js:XExt.Alert('Sample JS Link');", "onclick": "js:XExt.Alert('Sample JS Onclick');"},
    {"control": "html", "value":"HTML with both JavaScript Link + Onclick", "link": "js:XExt.Alert('Sample JS Link');", "onclick": "js:XExt.Alert('Sample JS Onclick');"}
  ]
}
</pre>
If both field.link and field.onclick are defined:<br/>
<br/>
For button / linkbutton controls, field.onclick will override field.link<br/>
For html / link controls, the onclick will be attached to the body element, not the link, so field.link will take precedence.<br/>
For html with field.value, onclick will not be applied.