diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AutocompleteObjectPropertyFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AutocompleteObjectPropertyFormGenerator.java new file mode 100644 index 000000000..1db0e99de --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/AutocompleteObjectPropertyFormGenerator.java @@ -0,0 +1,28 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators; + +import javax.servlet.http.HttpSession; + +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; + + +/** + * Auto complete object property form generator folded into DefualtObjectPropertyFormGenerator.java + * + */ +public class AutocompleteObjectPropertyFormGenerator extends DefaultObjectPropertyFormGenerator { + + @Override + public EditConfigurationVTwo getEditConfiguration(VitroRequest vreq, + HttpSession session) throws Exception { + //force auto complete + doAutoComplete = true; + + return super.getEditConfiguration(vreq, session); + } + + + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java index 69812870e..82aa1508f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/edit/n3editing/configuration/generators/DefaultObjectPropertyFormGenerator.java @@ -2,7 +2,7 @@ package edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators; -import static edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary.DISPLAY_ONT_MODEL; +import static edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary.*; import java.util.ArrayList; import java.util.Arrays; @@ -12,60 +12,86 @@ import java.util.Map; import javax.servlet.http.HttpSession; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServer; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocumentList; import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.Model; -import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty; +import edu.cornell.mannlib.vitro.webapp.beans.VClass; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary; +import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationUtils; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; -import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldOptions; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.FieldVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.fields.IndividualsViaObjectPropetyOptions; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.validators.AntiXssValidation; +import edu.cornell.mannlib.vitro.webapp.search.VitroSearchTermNames; import edu.cornell.mannlib.vitro.webapp.search.beans.ProhibitedFromSearch; +import edu.cornell.mannlib.vitro.webapp.search.solr.SolrSetup; +import edu.cornell.mannlib.vitro.webapp.utils.FrontEndEditingUtils; +import edu.cornell.mannlib.vitro.webapp.utils.FrontEndEditingUtils.EditMode; /** * Generates the edit configuration for a default property form. - * + * This handles the default object property auto complete. + * + * If a default property form is request and the number of indivdiuals + * found in the range is too large, the the auto complete setup and + * template will be used instead. */ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGenerator { - //TODO: bdc34 why does the DefaultObjectPropertyForm have all this data property stuff? - private Log log = LogFactory.getLog(DefaultObjectPropertyFormGenerator.class); - private boolean isObjectPropForm = false; + private Log log = LogFactory.getLog(DefaultObjectPropertyFormGenerator.class); private String subjectUri = null; private String predicateUri = null; - private String objectUri = null; - private String datapropKeyStr= null; - private int dataHash = 0; - - private String dataLiteral = null; + private String objectUri = null; + private String objectPropertyTemplate = "defaultPropertyForm.ftl"; - private String dataPropertyTemplate = "defaultDataPropertyForm.ftl"; + private String acObjectPropertyTemplate = "autoCompleteObjectPropForm.ftl"; + + protected boolean doAutoComplete = false; + protected boolean tooManyRangeIndividuals = false; + + protected long maxNonACRangeIndividualCount = 1000; + private static HashMap defaultsForXSDtypes ; static { defaultsForXSDtypes = new HashMap(); //defaultsForXSDtypes.put("http://www.w3.org/2001/XMLSchema#dateTime","2001-01-01T12:00:00"); defaultsForXSDtypes.put("http://www.w3.org/2001/XMLSchema#dateTime","#Unparseable datetime defaults to now"); } + + @Override public EditConfigurationVTwo getEditConfiguration(VitroRequest vreq, HttpSession session) throws Exception { + if(!EditConfigurationUtils.isObjectProperty(EditConfigurationUtils.getPredicateUri(vreq), vreq)) { + throw new Exception("DefaultObjectPropertyFormGenerator does not handle data properties."); + } + if( tooManyRangeOptions( vreq, session ) ){ + tooManyRangeIndividuals = true; + doAutoComplete = true; + } + //Check if create new and return specific edit configuration from that generator. if(DefaultAddMissingIndividualFormGenerator.isCreateNewIndividual(vreq, session)) { DefaultAddMissingIndividualFormGenerator generator = new DefaultAddMissingIndividualFormGenerator(); return generator.getEditConfiguration(vreq, session); } - + //TODO: Add a generator for delete: based on command being delete - propDelete.jsp //Generate a edit configuration for the default object property form and return it. //if(DefaultDeleteGenerator.isDelete( vreq,session)){ @@ -74,7 +100,33 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene return getDefaultObjectEditConfiguration(vreq, session); } - private EditConfigurationVTwo getDefaultObjectEditConfiguration(VitroRequest vreq, HttpSession session) throws Exception { + private boolean tooManyRangeOptions(VitroRequest vreq, HttpSession session ) throws SolrServerException { + List types = getRangeTypes(vreq); + SolrServer solrServer = SolrSetup.getSolrServer(session.getServletContext()); + + long count = 0; + for( String type:types){ + //solr query for type count. + SolrQuery query = new SolrQuery(); + if( VitroVocabulary.OWL_THING.equals( type )){ + query.setQuery( "*:*" ); + }else{ + query.setQuery( VitroSearchTermNames.RDFTYPE + ":" + type); + } + query.setRows(0); + + QueryResponse rsp = solrServer.query(query); + SolrDocumentList docs = rsp.getResults(); + long found = docs.getNumFound(); + count = count + found; + if( count > maxNonACRangeIndividualCount ) + break; + } + + return count > maxNonACRangeIndividualCount ; + } + + private EditConfigurationVTwo getDefaultObjectEditConfiguration(VitroRequest vreq, HttpSession session) throws Exception { EditConfigurationVTwo editConfiguration = new EditConfigurationVTwo(); //process subject, predicate, object parameters @@ -112,19 +164,21 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene //After the main processing is done, check if select from existing process processProhibitedFromSearch(vreq, session, editConfiguration); - //Form title and submit label now moved to edit configuration template - //TODO: check if edit configuration template correct place to set those or whether - //additional methods here should be used and reference instead, e.g. edit configuration template could call - //default obj property form.populateTemplate or some such method - //Select from existing also set within template itself + //Form title and submit label moved to template setTemplate(editConfiguration, vreq); editConfiguration.addValidator(new AntiXssValidation()); //Set edit key setEditKey(editConfiguration, vreq); + //Adding additional data, specifically edit mode - addFormSpecificData(editConfiguration, vreq); + if( doAutoComplete ){ + addFormSpecificDataForAC(editConfiguration, vreq, session); + }else{ + addFormSpecificData(editConfiguration, vreq); + } + return editConfiguration; } @@ -135,12 +189,10 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene private void setTemplate(EditConfigurationVTwo editConfiguration, VitroRequest vreq) { - String template = objectPropertyTemplate; - - if(EditConfigurationUtils.isDataProperty(editConfiguration.getPredicateUri(), vreq)){ - template = dataPropertyTemplate; - } - editConfiguration.setTemplate(template); + if( doAutoComplete ) + editConfiguration.setTemplate(acObjectPropertyTemplate); + else + editConfiguration.setTemplate(objectPropertyTemplate); } @@ -167,14 +219,11 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene //"object" : [ "objectVar" , "${objectUriJson}" , "URI"], if(EditConfigurationUtils.isObjectProperty(predicateUri, vreq)) { log.debug("This is an object property: " + predicateUri); - //not concerned about remainder, can move into default obj prop form if required - this.isObjectPropForm = true; this.initObjectParameters(vreq); this.processObjectPropForm(vreq, editConfiguration); } else { log.debug("This is a data property: " + predicateUri); - this.isObjectPropForm = false; - this.processDataPropForm(vreq, editConfiguration); + return; } } @@ -191,38 +240,21 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene //pretends this is a data property editing statement and throws an error //TODO: Check if null in case no object uri exists but this is still an object property } - - private void processDataPropForm(VitroRequest vreq, EditConfigurationVTwo editConfiguration) { - //bdc34 - throw new Error("DefaultObjectPropertyForm should not be doing data property editing"); - } - + //Get N3 required //Handles both object and data property private List generateN3Required(VitroRequest vreq) { List n3ForEdit = new ArrayList(); - String editString = "?subject ?predicate "; - if(this.isObjectPropForm) { - editString += "?objectVar"; - } else { - DataProperty prop = EditConfigurationUtils.getDataProperty(vreq); - String localName = prop.getLocalName(); - String dataLiteral = localName + "Edited"; - editString += "?"+dataLiteral; - } + String editString = "?subject ?predicate "; + editString += "?objectVar"; editString += " ."; n3ForEdit.add(editString); return n3ForEdit; } private List generateN3Optional() { - List n3Inverse = new ArrayList(); - //Note that for proper substitution, spaces expected between variables, i.e. string - //of n3 format - //Set only if object property form - if(this.isObjectPropForm) { - n3Inverse.add("?objectVar ?inverseProp ?subject ."); - } + List n3Inverse = new ArrayList(); + n3Inverse.add("?objectVar ?inverseProp ?subject ."); return n3Inverse; } @@ -258,22 +290,14 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene private void setUrisAndLiteralsOnForm(EditConfigurationVTwo editConfiguration, VitroRequest vreq) { List urisOnForm = new ArrayList(); List literalsOnForm = new ArrayList(); - if(EditConfigurationUtils.isDataProperty(EditConfigurationUtils.getPredicateUri(vreq), vreq)) { - //if data property set to data literal - literalsOnForm.add(dataLiteral); - } else { - //uris on form should be empty if data property - urisOnForm.add("objectVar"); - } + + //uris on form should be empty if data property + urisOnForm.add("objectVar"); + editConfiguration.setUrisOnform(urisOnForm); editConfiguration.setLiteralsOnForm(literalsOnForm); } - - private String getDataLiteral(VitroRequest vreq) { - DataProperty prop = EditConfigurationUtils.getDataProperty(vreq); - return prop.getLocalName() + "Edited"; - } - + //This is for various items private void setSparqlQueries(EditConfigurationVTwo editConfiguration) { //Sparql queries defining retrieval of literals etc. @@ -298,40 +322,29 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene HashMap map = new HashMap(); return map; } - - private void setFields(EditConfigurationVTwo editConfiguration, VitroRequest vreq, String predicateUri) throws Exception { - Map fields = new HashMap(); - if(EditConfigurationUtils.isObjectProperty(EditConfigurationUtils.getPredicateUri(vreq), vreq)) { - fields = getObjectPropertyField(editConfiguration, vreq); - } else { - throw new Exception("DefaultObjectPropertyFormGenerator does not handle data properties."); - } - - editConfiguration.setFields(fields); - } - - private Map getObjectPropertyField( - EditConfigurationVTwo editConfiguration, VitroRequest vreq) throws Exception { - Map fields = new HashMap(); + private void setFields(EditConfigurationVTwo editConfiguration, VitroRequest vreq, String predicateUri) throws Exception { FieldVTwo field = new FieldVTwo(); field.setName("objectVar"); - //queryForExisting is not being used anywhere in Field List validators = new ArrayList(); validators.add("nonempty"); field.setValidators(validators); - field.setOptions( new IndividualsViaObjectPropetyOptions( + if( ! doAutoComplete ){ + field.setOptions( new IndividualsViaObjectPropetyOptions( subjectUri, predicateUri, - objectUri)); - + objectUri)); + }else{ + field.setOptions(null); + } + + Map fields = new HashMap(); fields.put(field.getName(), field); - return fields; - - - } + + editConfiguration.setFields(fields); + } private void prepareForUpdate(VitroRequest vreq, HttpSession session, EditConfigurationVTwo editConfiguration) { //Here, retrieve model from @@ -390,7 +403,90 @@ public class DefaultObjectPropertyFormGenerator implements EditConfigurationGene formSpecificData.put("objectSelect", objectSelect); editConfiguration.setFormSpecificData(formSpecificData); } + + public void addFormSpecificDataForAC(EditConfigurationVTwo editConfiguration, VitroRequest vreq, HttpSession session) throws SolrServerException { + HashMap formSpecificData = new HashMap(); + //Get the edit mode + formSpecificData.put("editMode", getEditMode(vreq).toString().toLowerCase()); + + //We also need the type of the object itself + List types = getRangeTypes(vreq); + //if types array contains only owl:Thing, the search will not return any results + //In this case, set an empty array + if(types.size() == 1 && types.get(0).equals(VitroVocabulary.OWL_THING) ){ + types = new ArrayList(); + } + + formSpecificData.put("objectTypes", StringUtils.join(types, ",")); + + //Get label for individual if it exists + if(EditConfigurationUtils.getObjectIndividual(vreq) != null) { + String objectLabel = EditConfigurationUtils.getObjectIndividual(vreq).getName(); + formSpecificData.put("objectLabel", objectLabel); + } + + //TODO: find out if there are any individuals in the classes of objectTypes + formSpecificData.put("rangeIndividualsExist", rangeIndividualsExist(session,types) ); + + formSpecificData.put("sparqlForAcFilter", getSparqlForAcFilter(vreq)); + editConfiguration.setTemplate(acObjectPropertyTemplate); + editConfiguration.setFormSpecificData(formSpecificData); + } + + private Object rangeIndividualsExist(HttpSession session, List types) throws SolrServerException { + SolrServer solrServer = SolrSetup.getSolrServer(session.getServletContext()); + + boolean rangeIndividualsFound = false; + for( String type:types){ + //solr for type count. + SolrQuery query = new SolrQuery(); + query.setQuery( VitroSearchTermNames.RDFTYPE + ":" + type); + query.setRows(0); + + QueryResponse rsp = solrServer.query(query); + SolrDocumentList docs = rsp.getResults(); + if( docs.getNumFound() > 0 ){ + rangeIndividualsFound = true; + break; + } + } + + return rangeIndividualsFound; + } + + protected List getRangeTypes(VitroRequest vreq) { + Individual subject = EditConfigurationUtils.getSubjectIndividual(vreq); + String predicateUri = EditConfigurationUtils.getPredicateUri(vreq); + WebappDaoFactory wDaoFact = vreq.getWebappDaoFactory(); + List types = new ArrayList(); + List vclasses = new ArrayList(); + vclasses = wDaoFact.getVClassDao().getVClassesForProperty(subject.getVClassURI(),predicateUri); + for(VClass v: vclasses) { + types.add(v.getURI()); + } + return types; + } + + /** get the auto complete edit mode */ + public EditMode getEditMode(VitroRequest vreq) { + //In this case, the original jsp didn't rely on FrontEndEditingUtils + //but instead relied on whether or not the object Uri existed + String objectUri = EditConfigurationUtils.getObjectUri(vreq); + EditMode editMode = FrontEndEditingUtils.EditMode.ADD; + if(objectUri != null && !objectUri.isEmpty()) { + editMode = FrontEndEditingUtils.EditMode.EDIT; + + } + return editMode; + } - + public String getSparqlForAcFilter(VitroRequest vreq) { + String subject = EditConfigurationUtils.getSubjectUri(vreq); + String predicate = EditConfigurationUtils.getPredicateUri(vreq); + //Get all objects for existing predicate, filters out results from addition and edit + String query = "SELECT ?objectVar WHERE { " + + "<" + subject + "> <" + predicate + "> ?objectVar .} "; + return query; + } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java index d040d6856..f1a133757 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/templatemodels/edit/EditConfigurationTemplateModel.java @@ -95,8 +95,9 @@ public class EditConfigurationTemplateModel extends BaseTemplateModel { WebappDaoFactory wdf = vreq.getWebappDaoFactory(); for(String fieldName: editConfig.getFields().keySet()){ FieldVTwo field = editConfig.getField(fieldName); - if( field.getFieldOptions() == null ){ - continue; + if( field.getFieldOptions() == null ){ + //putting empty map in here because FM can't deal + pageData.put(fieldName, Collections.EMPTY_MAP); } Map optionsMap = SelectListGeneratorVTwo.getOptions(editConfig, fieldName, wdf); optionsMap = SelectListGeneratorVTwo.getSortedMap(optionsMap); diff --git a/webapp/web/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl b/webapp/web/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl new file mode 100644 index 000000000..1b81c82f2 --- /dev/null +++ b/webapp/web/templates/freemarker/edit/forms/autoCompleteObjectPropForm.ftl @@ -0,0 +1,148 @@ +<#-- $This file is distributed under the terms of the license in /doc/license.txt$ --> + +

autoCompleteObjectPropForm.ftl

+ +<#--Assign variables from editConfig--> +<#assign rangeOptions = editConfiguration.pageData.objectVar /> +<#-- +<#assign rangeOptionsExist = false /> +<#if (rangeOptions?keys?size > 0)> + <#assign rangeOptionsExist = true/> + + --> + + +<#assign rangeOptionsExist = true /> + +<#assign objectTypes = editConfiguration.pageData.objectTypes /> +<#assign objectTypesSize = objectTypes?length /> +<#assign objectTypesExist = false /> +<#assign multipleTypes = false /> +<#if (objectTypesSize > 1)> + <#assign objectTypesExist = true /> + +<#if objectTypes?contains(",")> + <#assign multipleTypes = true/> + +<#assign sparqlForAcFilter = editConfiguration.pageData.sparqlForAcFilter /> +<#assign editMode = editConfiguration.pageData.editMode /> +<#assign propertyNameForDisplay = "" /> +<#if editConfiguration.objectPropertyNameForDisplay?has_content> + <#assign propertyNameForDisplay = editConfiguration.objectPropertyNameForDisplay /> + +<#if editMode = "edit" > + <#assign titleVerb = "Edit" /> + <#assign objectLabel = editConfiguration.pageData.objectLabel /> + <#assign selectedObjectUri = editConfiguration.objectUri /> + <#assign submitButtonText = "Save Change" /> +<#else> + <#assign titleVerb = "Add" > + <#assign objectLabel = "" /> + <#assign selectedObjectUri = ""/> + <#assign submitButtonText = "Create Entry" /> + + +<#if editConfiguration.formTitle?contains("collaborator") > + <#assign formTitle = "Select an existing Collaborator for ${editConfiguration.subjectName}" /> +<#else> + <#assign formTitle = editConfiguration.formTitle /> + +<#--In order to fill out the subject--> +<#assign acFilterForIndividuals = "['" + editConfiguration.subjectUri + "']" /> + +range options ${rangeOptions?keys?size!"FUCK"} +<#if rangeOptionsExist >FUXINGEXIST!<#else>NOEXIST! + + +

${formTitle}

+ +<#if editConfiguration.propertySelectFromExisting = true> + <#if rangeOptionsExist = true > +
+ + <#if editConfiguration.propertyPublicDescription?has_content> +

${editConfiguration.propertyPublicDescription}

+ + + <#---This section should become autocomplete instead--> +

+ + +

+ +
+

+ + + (Verify this match or + change selection) +

+ +
+ + <#--The above section should be autocomplete--> + +

+ + + or + Cancel +

+
+ <#else> +

There are no entries in the system from which to select.

+ + +

 

+<#if editConfiguration.propertyOfferCreateNewOption = true> +<#include "defaultOfferCreateNewOptionForm.ftl"> + + + +<#if editConfiguration.propertySelectFromExisting = false && editConfiguration.propertyOfferCreateNewOption = false> +

This property is currently configured to prohibit editing.

+ + + +<#if editConfiguration.includeDeletionForm = true> +<#include "defaultDeletePropertyForm.ftl"> + + + +<#assign sparqlQueryUrl = "${urls.base}/ajax/sparqlQuery" > +<#--Passing in object types only if there are any types returned, otherwise +the parameter should not be passed at all to the solr search. +Also multiple types parameter set to true only if more than one type returned--> + +<#-- + edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.AutocompleteObjectPropertyFormGenerator + edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.generators.AddAttendeeRoleToPersonGenerator +--> + +${stylesheets.add('')} + ${stylesheets.add('')} + ${stylesheets.add('')} + + + ${scripts.add('', + '', + '', + '')} diff --git a/webapp/web/templates/freemarker/edit/forms/css/customForm.css b/webapp/web/templates/freemarker/edit/forms/css/customForm.css new file mode 100644 index 000000000..013db7917 --- /dev/null +++ b/webapp/web/templates/freemarker/edit/forms/css/customForm.css @@ -0,0 +1,149 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +form.customForm div { + clear: left; +} +form.customForm h6 { + font-size: 110%; +} +form.customForm div.addNewLink { + float: left; + clear: none; + margin-left: 5em; + margin-top: .9em; + width: 200px; + padding: .8em; + border: 1px solid #9c9c9c; + display: none; /* Hide if Javascript disabled. Javascript will show. */ +} +form.customForm .existing span.requiredHint, +form.customForm .new span.requiredHint { + display: none; /* Hide if Javascript disabled. Javascript will show. */ +} +form.customForm .existing { + float: left; + clear: none; +} +.existingOrNew { + font-style: italic; + margin-left: 1em; +} +.new { + padding: .6em 0 .6em 1.5em; + border: 1px solid #9c9c9c; + width: 300px; +} +form.customForm p.inline input, +form.customForm p.inline label { + float: left; + clear: left; +} +form.customForm p.inline.year input { + margin-top: -1.75em; +} +input, +select, +form.customForm p { + margin-top: 0; + padding-top: 0; + margin-bottom: 0; + padding-bottom: 0; +} +option { + padding: 0 2px; +} +form.customForm .hint { + color: #9c9c9c; + font-weight: normal; +} +form.customForm .requiredHint { + color: #c00; + font-weight: normal; + font-size: small; +} +form.customForm #requiredLegend { + font-style: italic; + margin-top: .5em; +} +form.customForm p.validationError { + clear: both; + margin-bottom: 1.8em; + margin-top: 0; + padding-top: 0; + padding-left: .4em; + font-size: .8em; +} +form.customForm a.close { + float: right; + margin-right: 1em; + font-size: 90%; +} +form.customForm a.close:link, +form.customForm a.close:visited { + border-color: #ff7700; + color: #ff7700; +} +form.customForm textarea { + width: 30%; +} +div.acSelection { + margin-bottom: 15px; +} +#ie67DisableWrapper { + display: none; +} +form.customForm input.concept-search { + float: right; + margin-right: 1em; + margin-bottom: 1em; + background-color: #317e95; +} +form.customForm h4.services { + margin-bottom: -5px; + margin-top: -12px; +} +form.customForm p.inline-search { + float: left; + clear: left; +} +form.customForm p.inline-search #searchTerm{ + margin-top: 6px; +} +/* <------ DATE TIME*/ +form.customForm label.dateTime { + display: inline; +} +form.customForm fieldset { + display: inline; +} +fieldset.dateTime label { + display: inline; +} +fieldset.dateTime select { + margin-top: 0; +} +/* ---------------------------------- */ +/* ----- FOR MANAGE PUBLICATIONS ---- */ +/* ---------------------------------- */ +section#pubsContainer { + margin-top:-8px; + padding-left:25px; +} +section#pubsContainer ul { + padding-left:25px; + text-indent:-25px; +} +section#pubsContainer li { + margin-bottom:6px; + line-height:20px; +} +section#pubsContainer input { + margin-right:8px; +} +/* ---------------------------------- */ +/* --------- MISCELLANEOUS -------- */ +/* ------------------------------- */ +img#indicator { + padding-left:60px; +} + diff --git a/webapp/web/templates/freemarker/edit/forms/css/customFormWithAutocomplete.css b/webapp/web/templates/freemarker/edit/forms/css/customFormWithAutocomplete.css new file mode 100644 index 000000000..54218bd21 --- /dev/null +++ b/webapp/web/templates/freemarker/edit/forms/css/customFormWithAutocomplete.css @@ -0,0 +1,50 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +@import url("autocomplete.css"); + +/* Although Javascript hides these on page load, hide here as well to avoid the flash on page load. + This needs to be removed to support a non-JS version of the form. */ +.fullViewOnly, +#submit, +.or, +#requiredLegend { + display: none; +} +form.customForm p.inline label { + display: inline; + clear: none; + float: none; + margin-right: 1em; +} +.verifyMatch { + margin-left: .5em; +} +form.customForm h4 { + margin-top: 1em; + margin-bottom: .75em; +} +.acSelector[disabled="disabled"]{ + border-width: 0; + background: none; + color: #000; +} +.disabledSubmit { + cursor: default ! important; +} +span.readOnly { + color: #000; +} +/* special styles for addPublicationToPerson.ftl + volume, number, issue fields and start/end page fields */ +label.vniLabels { + padding-left: 50px; +} +input.vniInputs { + margin-left: 57px; +} +label.sepLabels { + padding-left: 31px; +} +input.sepInputs { + margin-left: 57px; +} diff --git a/webapp/web/templates/freemarker/edit/forms/js/customFormWithAutocomplete.js b/webapp/web/templates/freemarker/edit/forms/js/customFormWithAutocomplete.js new file mode 100644 index 000000000..a406d54e1 --- /dev/null +++ b/webapp/web/templates/freemarker/edit/forms/js/customFormWithAutocomplete.js @@ -0,0 +1,642 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +var customForm = { + + /* *** Initial page setup *** */ + + onLoad: function() { + + if (this.disableFormInUnsupportedBrowsers()) { + return; + } + this.mixIn(); + this.initObjects(); + this.initPage(); + }, + + disableFormInUnsupportedBrowsers: function() { + var disableWrapper = $('#ie67DisableWrapper'); + + // Check for unsupported browsers only if the element exists on the page + if (disableWrapper.length) { + if (vitro.browserUtils.isIELessThan8()) { + disableWrapper.show(); + $('.noIE67').hide(); + return true; + } + } + return false; + }, + + mixIn: function() { + // Mix in the custom form utility methods + $.extend(this, vitro.customFormUtils); + + // Get the custom form data from the page + $.extend(this, customFormData); + }, + + // On page load, create references for easy access to form elements. + // NB These must be assigned after the elements have been loaded onto the page. + initObjects: function(){ + + this.form = $('form.customForm'); + this.fullViewOnly = $('.fullViewOnly'); + this.button = $('#submit'); + this.requiredLegend = $('#requiredLegend'); + this.typeSelector = this.form.find('select#typeSelector'); + this.typeSelectorInput = this.form.find('input#typeSelectorInput'); + this.typeSelectorSpan = this.form.find('span#typeSelectorSpan'); + this.or = $('span.or'); + this.cancel = this.form.find('.cancel'); + this.acHelpTextClass = 'acSelectorWithHelpText'; + // this.verifyMatch is referenced in bindEventListeners to size and open + // the verify popup window. Although there could be multiple verifyMatch objects + // selecting one and binding the event works for all of them + this.verifyMatch = this.form.find('.verifyMatch'); + + // find all the acSelector input elements + this.acSelectors = [] ; + + this.form.find('.acSelector').each(function() { + customForm.acSelectors.push($(this)); + }); + + // find all the acSelection div elements + this.acSelections = new Object(); + + this.form.find('.acSelection').each(function() { + var groupName = $(this).attr('acGroupName'); + customForm.acSelections[groupName] = $(this); + }); + + // 2-stage forms with only one ac field will not have the acTypes defined + // so create an object for when the user selects a type via the typeSelector + if ( this.acTypes == undefined || this.acTypes == null ) { + this.acTypes = new Object(); + } + + // forms with multi ac fields will have this defined in customFormData + // this is helpful when the type to display is not a single word, like "Subject Area" + this.hasMultipleTypeNames = false; + if ( this.multipleTypeNames != undefined || this.multipleTypeNames != null ) { + this.hasMultipleTypeNames = true; + } + // Used with the cancel link. If the user cancels after a type selection, this check + // ensures that any a/c fields (besides the one associated with the type) will be reset + this.clearAcSelections = false; + + }, + + // Set up the form on page load + initPage: function() { + + if (!this.editMode) { + this.editMode = 'add'; // edit vs add: default to add + } + + //Flag to clear label of selected object from autocomplete on submission + //This is used in the case where the label field is submitted only when a new object is being created + if(!this.flagClearLabelForExisting) { + this.flagClearLabelForExisting = null; + } + + if (!this.formSteps) { // Don't override formSteps specified in form data + if ( !this.fullViewOnly.length || this.editMode === 'edit' || this.editMode === 'repair' ) { + this.formSteps = 1; + // there may also be a 3-step form - look for this.subTypeSelector + } + else { + this.formSteps = 2; + } + } + + if(!this.doNotRemoveOriginalObject) { + this.doNotRemoveOriginalObject = false; + } + + this.bindEventListeners(); + + $.each(this.acSelectors, function() { + customForm.initAutocomplete($(this)); + }); + + this.initElementData(); + + this.initFormView(); + + }, + + initFormView: function() { + + var typeVal = this.typeSelector.val(); + + // Put this case first, because in edit mode with + // validation errors we just want initFormFullView. +// if ((!this.supportEdit) && (this.editMode == 'edit' || this.editMode == 'repair')) { + if (this.editMode == 'edit' || this.editMode == 'repair') { + this.initFormWithValidationErrors(); + this.initFormFullView(); + } + else if (this.findValidationErrors()) { + this.initFormWithValidationErrors(); + } + + // If type is already selected when the page loads (Firefox retains value + // on a refresh), go directly to full view. Otherwise user has to reselect + // twice to get to full view. + else if ( this.formSteps == 1 || typeVal.length ) { + this.initFormFullView(); + } + else { + this.initFormTypeView(); + } + }, + + initFormTypeView: function() { + + this.setType(); // empty any previous values (perhaps not needed) + this.hideFields(this.fullViewOnly); + this.button.hide(); + this.or.hide(); + this.requiredLegend.hide(); + + this.cancel.unbind('click'); + }, + + initFormFullView: function() { + + this.setType(); + this.fullViewOnly.show(); + this.or.show(); + this.requiredLegend.show(); + this.button.show(); + this.setLabels(); + + // Set the initial autocomplete help text in the acSelector fields. + $.each(this.acSelectors, function() { + customForm.addAcHelpText($(this)); + }); + + this.cancel.unbind('click'); + if (this.formSteps > 1) { + this.cancel.click(function() { + customForm.clearFormData(); // clear any input and validation errors + customForm.initFormTypeView(); + customForm.clearAcSelections = true; + return false; + }); + // In one-step forms, if there is a type selection field, but no value is selected, + // hide the acSelector field. The type selection must be made first so that the + // autocomplete type can be determined. If a type selection has been made, + // unhide the acSelector field. + } else if (this.typeSelector.length) { + this.typeSelector.val() ? this.fullViewOnly.show() : this.hideFields(this.fullViewOnly); + } + if ( this.acSelectOnly ) { + this.disableSubmit(); + } + }, + + initFormWithValidationErrors: function() { + // Call initFormFullView first, because showAutocompleteSelection needs + // acType, which is set in initFormFullView. + this.initFormFullView(); + + $.each(this.acSelectors, function() { + var $acSelection = customForm.acSelections[$(this).attr('acGroupName')]; + var uri = $acSelection.find('input.acUriReceiver').val(), + label = $(this).val(); + if (uri && uri != ">SUBMITTED VALUE WAS BLANK<") { + customForm.showAutocompleteSelection(label, uri, $(this)); + } + }); + + }, + + // Bind event listeners that persist over the life of the page. Event listeners + // that depend on the view should be initialized in the view setup method. + bindEventListeners: function() { + + this.typeSelector.change(function() { + var typeVal = $(this).val(); + this.acCache = {}; + + // If an autocomplete selection has been made, undo it. + // NEED TO LINK THE TYPE SELECTOR TO THE ACSELECTOR IT'S ASSOCIATED WITH + // BECAUSE THERE COULD BE MORE THAN ONE AC FIELD. ASSOCIATION IS MADE VIA + // THE SPECIAL "acGroupName" ATTRIBUTE WHICH IS SHARED AMONG THE SELECT AND + // THE INPUT AND THE AC SELECTION DIV. + if (customForm.editMode != "edit") { + customForm.undoAutocompleteSelection($(this)); + } + // Reinitialize view. If no type selection in a two-step form, go back to type view; + // otherwise, reinitialize full view. + if (!typeVal.length && customForm.formSteps > 1) { + customForm.initFormTypeView(); + } + else { + customForm.initFormFullView(); + } + }); + + this.verifyMatch.click(function() { + window.open($(this).attr('href'), 'verifyMatchWindow', 'width=640,height=640,scrollbars=yes,resizable=yes,status=yes,toolbar=no,menubar=no,location=no'); + return false; + }); + + // loop through all the acSelectors + $.each(this.acSelectors, function() { + $(this).focus(function() { + customForm.deleteAcHelpText($(this)); + }); + $(this).blur(function() { + customForm.addAcHelpText($(this)); + }); + }); + + this.form.submit(function() { + //TODO: update the following + //custom submission for edit mode in case where existing object should not remove original object + //if edit mode and custom flag and original uri not equivalent to new uri, then + //clear out label field entirely + //originally checked edit mode but want to add to work the same way in case an existing object + //is selected since the n3 now governs how labels + if(customForm.flagClearLabelForExisting != null) { + //Find the elements that have had autocomplete executed, tagged by class "userSelected" + customForm.form.find('.acSelection.userSelected').each(function() { + var groupName = $(this).attr("acGroupName"); + var inputs = $(this).find("input.acUriReceiver"); + //if user selected, then clear out the label since we only + //want to submit the label as value on form if it's a new label + if(inputs.length && $(inputs.eq(0)).attr(customForm.flagClearLabelForExisting)) { + var $selectorInput = $("input.acSelector[acGroupName='" + groupName + "']"); + var $displayInput = $("input.display[acGroupName='" + groupName + "']"); + $displayInput.val($selectorInput.val()); + $selectorInput.val(''); + } + }); + } + + customForm.deleteAcHelpText(); + }); + }, + + initAutocomplete: function(selectedObj) { + this.getAcFilter(); + //If specific individuals are to be filtered out, add them here + //to the filtering list + this.getAcFilterForIndividuals(); + this.acCache = {}; + + $(selectedObj).autocomplete({ + minLength: 3, + source: function(request, response) { + //Reset the URI of the input to one that says new uri required + //That will be overwritten if value selected from autocomplete + //We do this everytime the user types anything in the autocomplete box + customForm.initDefaultBlankURI(selectedObj); + if (request.term in customForm.acCache) { + // console.log('found term in cache'); + response(customForm.acCache[request.term]); + return; + } + // console.log('not getting term from cache'); + $.ajax({ + url: customForm.acUrl, + dataType: 'json', + data: { + term: request.term, + type: customForm.acTypes[$(selectedObj).attr('acGroupName')], + multipleTypes:(customForm.acMultipleTypes == undefined || customForm.acMultipleTypes == null)? null: customForm.acMultipleTypes + }, + complete: function(xhr, status) { + // Not sure why, but we need an explicit json parse here. + var results = $.parseJSON(xhr.responseText), + filteredResults = customForm.filterAcResults(results); + customForm.acCache[request.term] = filteredResults; + response(filteredResults); + } + }); + }, + select: function(event, ui) { + customForm.showAutocompleteSelection(ui.item.label, ui.item.uri, $(selectedObj)); + } + }); + }, + + // Store original or base text with elements that will have text substitutions. + // Generally the substitution cannot be made on the current value, since that value + // may have changed from the original. So we store the original text with the element to + // use as a base for substitutions. + initElementData: function() { + + this.placeholderText = '###'; + this.labelsWithPlaceholders = this.form.find('label, .label').filter(function() { + return $(this).html().match(customForm.placeholderText); + }); + this.labelsWithPlaceholders.each(function(){ + $(this).data('baseText', $(this).html()); + }); + + this.button.data('baseText', this.button.val()); + + }, + //get autocomplete filter with sparql query + getAcFilter: function() { + + if (!this.sparqlForAcFilter) { + //console.log('autocomplete filtering turned off'); + this.acFilter = null; + return; + } + + //console.log("sparql for autocomplete filter: " + this.sparqlForAcFilter); + + // Define this.acFilter here, so in case the sparql query fails + // we don't get an error when referencing it later. + this.acFilter = []; + $.ajax({ + url: customForm.sparqlQueryUrl, + dataType: "json", + data: { + query: customForm.sparqlForAcFilter + }, + success: function(data, status, xhr) { + customForm.setAcFilter(data); + } + }); + }, + + setAcFilter: function(data) { + + var key = data.head.vars[0]; + + $.each(data.results.bindings, function() { + customForm.acFilter.push(this[key].value); + }); + }, + + filterAcResults: function(results) { + var filteredResults; + + if (!this.acFilter || !this.acFilter.length) { + //console.log('no autocomplete filtering applied'); + return results; + } + + filteredResults = []; + $.each(results, function() { + if ($.inArray(this.uri, customForm.acFilter) == -1) { + //console.log('adding ' + this.label + ' to filtered results'); + filteredResults.push(this); + } + else { + //console.log('filtering out ' + this.label); + } + }); + return filteredResults; + }, + //To filter out specific individuals, not part of a query + //Pass in list of individuals to be filtered out + getAcFilterForIndividuals: function() { + + if (!this.acFilterForIndividuals || !this.acFilterForIndividuals.length) { + this.acFilterForIndividuals = null; + return; + } + //add this list to the ac filter list + customForm.acFilter = customForm.acFilter.concat(this.acFilterForIndividuals); + + }, + + showAutocompleteSelection: function(label, uri, selectedObj) { + // hide the acSelector field and set it's value to the selected ac item + this.hideFields($(selectedObj).parent()); + $(selectedObj).val(label); + + var $acDiv = this.acSelections[$(selectedObj).attr('acGroupName')]; + + // provides a way to monitor selection in other js files, e.g. to hide fields upon selection + $acDiv.addClass("userSelected"); + + // If the form has a type selector, add type name to label in add mode. In edit mode, use typeSelectorSpan + // html. The second case is an "else if" and not an else because the template may not be passing the label + // to the acSelection macro or it may not be using the macro at all and the label is hard-coded in the html. + if ( this.typeSelector.length && ($acDiv.attr('acGroupName') == this.typeSelector.attr('acGroupName')) ) { + $acDiv.find('label').html('Selected ' + this.typeName + ':'); + } + else if ( this.typeSelectorSpan.html() && ($acDiv.attr('acGroupName') == this.typeSelectorInput.attr('acGroupName')) ) { + $acDiv.find('label').html('Selected ' + this.typeSelectorSpan.html() + ':'); + } + else if ( $acDiv.find('label').html() == '' ) { + $acDiv.find('label').html('Selected ' + this.multipleTypeNames[$(selectedObj).attr('acGroupName')] + ':'); + } + + $acDiv.show(); + $acDiv.find("input").val(uri); + $acDiv.find("span").html(label); + $acDiv.find("a.verifyMatch").attr('href', this.baseHref + uri); + + $changeLink = $acDiv.find('a.changeSelection'); + $changeLink.click(function() { + customForm.undoAutocompleteSelection($acDiv); + }); + + if ( this.acSelectOnly ) { + //On initialization in this mode, submit button is disabled + this.enableSubmit(); + } + + }, + + undoAutocompleteSelection: function(selectedObj) { + // The test is not just for efficiency: undoAutocompleteSelection empties the acSelector value, + // which we don't want to do if user has manually entered a value, since he may intend to + // change the type but keep the value. If no new value has been selected, form initialization + // below will correctly empty the value anyway. + + var $acSelectionObj = null; + var $acSelector = null; + + // Check to see if the parameter is the typeSelector. If it is, we need to get the acSelection div + // that is associated with it. Also, when the type is changed, we need to determine whether the user + // has selected an existing individual in the corresponding name field or typed the label for a new + // individual. If the latter, we do not want to clear the value on type change. The clearAcSelectorVal + // boolean controls whether the acSelector value gets cleared. + + var clearAcSelectorVal = true; + + if ( $(selectedObj).attr('id') == "typeSelector" ) { + $acSelectionObj = customForm.acSelections[$(selectedObj).attr('acGroupName')]; + if ( $acSelectionObj.is(':hidden') ) { + clearAcSelectorVal = false; + } + // if the type is being changed after a cancel, any additional a/c fields that may have been set + // by the user should be "undone". Only loop through these if this is not the initial type selection + if ( customForm.clearAcSelections ) { + $.each(customForm.acSelections, function(i, acS) { + var $checkSelection = customForm.acSelections[i]; + if ( $checkSelection.is(':hidden') && $checkSelection.attr('acGroupName') != $acSelectionObj.attr('acGroupName') ) { + customForm.resetAcSelection($checkSelection); + $acSelector = customForm.getAcSelector($checkSelection); + $acSelector.parent('p').show(); + } + }); + } + } + else { + $acSelectionObj = $(selectedObj); + } + + $acSelector = this.getAcSelector($acSelectionObj); + $acSelector.parent('p').show(); + this.resetAcSelection($acSelectionObj); + if ( clearAcSelectorVal == true ) { + $acSelector.val(''); + $("input.display[acGroupName='" + $acSelectionObj.attr('acGroupName') + "']").val(""); + } + customForm.addAcHelpText($acSelector); + + //Resetting so disable submit button again for object property autocomplete + if ( this.acSelectOnly ) { + this.disableSubmit(); + } + this.clearAcSelections = false; + }, + + // this is essentially a subtask of undoAutocompleteSelection + resetAcSelection: function(selectedObj) { + this.hideFields($(selectedObj)); + $(selectedObj).removeClass('userSelected'); + $(selectedObj).find("input.acUriReceiver").val(this.blankSentinel); + $(selectedObj).find("span").text(''); + $(selectedObj).find("a.verifyMatch").attr('href', this.baseHref); + }, + + // loops through the array of acSelector fields and returns the one + // associated with the selected object + getAcSelector: function(selectedObj){ + var $selector = null + $.each(this.acSelectors, function() { + if ( $(this).attr('acGroupName') == $(selectedObj).attr('acGroupName') ) { + $selector = $(this); + } + }); + return $selector; + }, + + // Set type uri for autocomplete, and type name for labels and button text. + // Note: we still need this in edit mode, to set the text values. + setType: function() { + var selectedType; + + // If there's no type selector, these values have been specified in customFormData, + // and will not change over the life of the form. + if (!this.typeSelector.length) { + if ( this.editMode == 'edit' && (this.typeSelectorSpan.html() != null && this.typeSelectorInput.val() != null) ) { + this.typeName = this.typeSelectorSpan.html(); + this.acTypes[this.typeSelectorInput.attr('acGroupName')] = this.typeSelectorInput.val(); + } + return; + } + + selectedType = this.typeSelector.find(':selected'); + var acTypeKey = this.typeSelector.attr('acGroupName'); + if (selectedType.val().length) { + this.acTypes[acTypeKey] = selectedType.val(); + this.typeName = selectedType.html(); + } + // reset to empty values; may not need + else { + delete this.acTypes[acTypeKey]; + this.typeName = ''; + } + }, + + // Set field labels based on type selection. Although these won't change in edit + // mode, it's easier to specify the text here than in the jsp. + setLabels: function() { + var typeName = this.getTypeNameForLabels(); + + this.labelsWithPlaceholders.each(function() { + var newLabel = $(this).data('baseText').replace(customForm.placeholderText, typeName); + $(this).html(newLabel); + }); + + }, + + getTypeNameForLabels: function(selectedObj) { + // If this.acType is empty, we are either in a one-step form with no type yet selected, + // or in repair mode in a two-step form with no type selected. Use the default type + // name specified in the form data. + if ( !selectedObj || !this.hasMultipleTypeNames ) { + return this.acTypes ? this.typeName : this.capitalize(this.defaultTypeName); + } + else if ( selectedObj && ( $(selectedObj).attr('acGroupName') == this.typeSelector.attr('acGroupName') ) ) { + return this.acTypes ? this.typeName : this.capitalize(this.defaultTypeName); + } + else { + var name = customForm.multipleTypeNames[$(selectedObj).attr('id')]; + return this.capitalize(name); + } + }, + + // Set the initial help text that appears in the autocomplete field and change the class name + addAcHelpText: function(selectedObj) { + var typeText; + // First case applies on page load; second case applies when the type gets changed. With multiple + // ac fields there are cases where we also have to check if the help text is already there + if (!$(selectedObj).val() || $(selectedObj).hasClass(this.acHelpTextClass) || $(selectedObj).val().substring(0, 18) == "Select an existing" ) { + typeText = this.getTypeNameForLabels($(selectedObj)); + var helpText = "Select an existing " + typeText + " or create a new one."; + //Different for object property autocomplete + if ( this.acSelectOnly ) { + helpText = "Select an existing " + typeText; + } + $(selectedObj).val(helpText) + .addClass(this.acHelpTextClass); + } + }, + + + deleteAcHelpText: function(selectedObj) { + // on submit, no selectedObj gets passed, so we need to check for this + if ( selectedObj ) { + if ($(selectedObj).hasClass(this.acHelpTextClass)) { + $(selectedObj).val('') + .removeClass(this.acHelpTextClass); + } + } + else { + $.each(this.acSelectors, function() { + if ($(this).hasClass(customForm.acHelpTextClass)) { + $(this).val('') + .removeClass(customForm.acHelpTextClass); + } + }); + } + }, + disableSubmit: function() { + //Disable submit button until selection made + this.button.attr('disabled', 'disabled'); + this.button.addClass('disabledSubmit'); + }, + enableSubmit:function() { + this.button.removeAttr('disabled'); + this.button.removeClass('disabledSubmit'); + }, + initDefaultBlankURI:function(selectedObj) { + //get uri input for selected object and set to value specified as "blank sentinel" + //If blank sentinel is neither null nor an empty string, this means if the user edits an + //existing relationship to an object and does not select anything from autocomplete + //from that object, the old relationship will be removed in n3 processing + var $acDiv = this.acSelections[$(selectedObj).attr('acGroupName')]; + $acDiv.find("input").val(customForm.blankSentinel); + } + +}; + +$(document).ready(function() { + customForm.onLoad(); +});