/**
* Given a field name, this will return its field info or null if it doesn't exist.
* @param {String} field_name
* @return {Object}
*/
function drupalgap_field_info_field(field_name) {
return drupalgap.field_info_fields && drupalgap.field_info_fields[field_name] ?
drupalgap.field_info_fields[field_name] : null;
}
/**
* Returns info on all fields or null if they don't exist.
* @return {Object}
*/
function drupalgap_field_info_fields() {
return drupalgap.field_info_fields ? drupalgap.field_info_fields : null;
}
/**
* Given an entity type, field name, and bundle name this will return a JSON
* object with data for the specified field name.
* @param {String} entity_type
* @param {String} field_name
* @param {String} bundle_name
* @return {Object}
*/
function drupalgap_field_info_instance(entity_type, field_name, bundle_name) {
try {
var instances = drupalgap_field_info_instances(entity_type, bundle_name);
var warningPrefix = 'WARNING: drupalgap_field_info_instance - ';
if (!instances) {
console.log(
warningPrefix +
'instance was null for entity (' + entity_type + ') bundle (' + bundle_name + ') using field (' + field_name + ')'
);
return null;
}
if (!instances[field_name]) {
console.log(
warningPrefix +
'"' + field_name + '" does not exist for entity (' + entity_type + ') bundle (' + bundle_name + ')'
);
return null;
}
return instances[field_name];
}
catch (error) { console.log('drupalgap_field_info_instance - ' + error); }
}
/**
* Given an entity type and/or a bundle name, this returns the field info
* instances for the entity or the bundle.
* @param {String} entity_type
* @param {String} bundle_name
* @return {Object}
*/
function drupalgap_field_info_instances(entity_type, bundle_name) {
try {
var instances = drupalgap.field_info_instances;
if (!instances) { return null; }
var field_info_instances = null;
// If there is no bundle, pull the fields out of the wrapper.
// @TODO there appears to be a special case with commerce_products, in that
// they aren't wrapped like normal entities (see the else statement when a
// bundle name isn't present). Or do we have a bug here, and we shouldn't
// be expecting the wrapper in the first place?
if (!bundle_name) {
field_info_instances = entity_type == 'commerce_product' ?
instances[entity_type] : instances[entity_type][entity_type];
}
else if (typeof instances[entity_type] !== 'undefined') {
field_info_instances = instances[entity_type][bundle_name];
}
return field_info_instances;
}
catch (error) { console.log('drupalgap_field_info_instances - ' + error); }
}
/**
* Given an entity type, bundle, form and entity, this will add the
* entity's fields to the given form.
* @param {String} entity_type
* @param {String} bundle
* @param {Object} form
* @param {Object} entity
*/
function drupalgap_field_info_instances_add_to_form(entity_type, bundle, form, entity) {
try {
// Grab the field info instances for this entity type and bundle.
var fields = drupalgap_field_info_instances(entity_type, bundle);
// If there is no bundle, pull the fields out of the wrapper.
//if (!bundle) { fields = fields[entity_type]; }
// Use the default language, unless the entity has one specified.
var language = language_default();
if (entity && entity.language) { language = entity.language; }
// Iterate over each field in the entity and add it to the form. If there is
// a value present in the entity, then set the field's form element default
// value equal to the field value.
if (fields) {
for (var name in fields) {
if (!fields.hasOwnProperty(name)) { continue; }
var field = fields[name];
// The user registration form is a special case, in that we only want
// to place fields that are set to display on the user registration
// form. Skip any fields not set to display.
if (form.id == 'user_register_form' &&
!field.settings.user_register_form) {
continue;
}
var field_info = drupalgap_field_info_field(name);
if (field_info) {
form.elements[name] = {
type: field_info.type,
title: field.label,
required: field.required,
description: field.description
};
var default_value = field.default_value;
var cardinality = parseInt(field_info.cardinality);
if (cardinality == -1) {
cardinality = 1; // we'll just add one element for now, until we
// figure out how to handle the 'add another
// item' feature.
}
if (entity && entity[name] && entity[name].length != 0) {
// Make sure the field has some type of language code on it, or just skip it. An entity will sometimes have
// a language code that a field doesn't have, so fall back to und on the field if the language code isn't
// present.
if (!entity[name][language]) {
if (!entity[name].und) { continue; }
language = 'und';
}
if (!form.elements[name][language]) { form.elements[name][language] = {}; }
for (var delta = 0; delta < cardinality; delta++) {
// @TODO - is this where we need to use the idea of the
// value_callback property present in Drupal's FAPI? That way
// each element knows how to map the entity data to its element
// value property.
if (
entity[name][language][delta] &&
typeof entity[name][language][delta].value !== 'undefined'
) { default_value = entity[name][language][delta].value; }
// If the default_value is null, set it to an empty string.
if (default_value == null) { default_value = ''; }
// Note, not all fields have a language code to use here, e.g. taxonomy term reference fields do not.
form.elements[name][language][delta] = {
value: default_value
};
// Place the field item onto the element.
if (entity[name][language][delta]) {
form.elements[name][language][delta].item =
entity[name][language][delta];
}
}
// Set the language back to the entity's language in case it was temporarily changed because of an un
// translated field.
if (entity && entity.language) { language = entity.language; }
}
// Give module's a chance to alter their own element during the form
// build, that way element properties will be saved to local storage
// and then available during hook_field_widget_form() and the form
// submission process.
var fn = field.widget.module + '_field_info_instance_add_to_form';
if (function_exists(fn)) {
window[fn](entity_type, bundle, form, entity, form.elements[name]);
}
}
}
}
}
catch (error) {
console.log('drupalgap_field_info_instances_add_to_form - ' + error);
}
}
/**
* Given a field name, this will return the key that should be used when
* setting its value on an entity. If the field name is not a field, it returns
* false.
* @param {String} field_name
* @return {String}
*/
function drupalgap_field_key(field_name) {
try {
// Determine the key to use for the value. By default, most fields
// use 'value' as the key.
var key = false;
var field_info = drupalgap_field_info_field(field_name);
if (field_info) {
key = 'value';
// Images use fid as the key.
if (field_info.module == 'image' && field_info.type == 'image') {
key = 'fid';
}
else if (
field_info.module == 'taxonomy' &&
field_info.type == 'taxonomy_term_reference'
) { key = 'tid'; }
}
return key;
}
catch (error) { console.log('drupalgap_field_key - ' + error); }
}
/**
* Implements hook_field_formatter_view().
* @param {String} entity_type
* @param {Object} entity
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Object} items
* @param {*} display
* @return {Object}
*/
function list_field_formatter_view(entity_type, entity, field, instance, langcode, items, display) {
try {
var element = {};
if (!empty(items)) {
for (var delta in items) {
if (!items.hasOwnProperty(delta)) { continue; }
var item = items[delta];
var markup = '';
// list_default or list_key
if (display.type == 'list_default') {
markup = instance.settings.allowed_values[item.value];
// Single on/off checkboxes need an empty space as their markup so
// just the label gets rendered.
if (
instance.type == 'list_boolean' &&
field.widget.type == 'options_onoff'
) { markup = ' '; }
}
else { markup = item.value; }
element[delta] = { markup: markup };
}
}
return element;
}
catch (error) { console.log('list_field_formatter_view - ' + error); }
}
/**
* Implements hook_assemble_form_state_into_field().
* @param {Object} entity_type
* @param {String} bundle
* @param {String} form_state_value
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Number} delta
* @param {Object} field_key
*
* @return {*}
*/
function list_assemble_form_state_into_field(entity_type, bundle, form_state_value, field, instance, langcode, delta, field_key) {
try {
var result = form_state_value;
switch (field.type) {
case 'list_boolean':
// For single on/off checkboxes, if the checkbox is unchecked, then we
// send a null value on the language code. We know the checkbox is
// unchecked if the form_state_value is equal to the first allowed value
// on the field.
if (instance.widget.type == 'options_onoff') {
var index = 0;
var on = true;
for (var value in field.settings.allowed_values) {
if (!field.settings.allowed_values.hasOwnProperty(value)) { continue; }
var label = field.settings.allowed_values[value];
if (form_state_value == value && index == 0) {
on = false;
break;
}
index++;
}
if (!on) {
field_key.use_delta = false;
field_key.use_wrapper = false;
result = null;
}
}
else {
console.log(
'WARNING: list_assemble_form_state_into_field - unknown widget (' +
field.type +
') on list_boolean'
);
}
break;
case 'list_text':
// For radio buttons on the user entity form, field values must be
// "flattened", i.e. this field_foo: { und: [ { value: 123 }]}, should
// be turned into field_foo: { und: 123 }
if (
entity_type == 'user' &&
instance.widget.type == 'options_buttons'
) {
field_key.use_delta = false;
field_key.use_wrapper = false;
}
break;
default:
console.log(
'WARNING: list_assemble_form_state_into_field - unknown type (' +
field.type +
')'
);
break;
}
return result;
}
catch (error) {
console.log('list_assemble_form_state_into_field - ' + error);
}
}
/**
* Implements hook_views_exposed_filter().
* @param {Object} form
* @param {Object} form_state
* @param {Object} element
* @param {Object} filter
* @param {Object} field
*/
function list_views_exposed_filter(form, form_state, element, filter, field) {
try {
//console.log('list_views_exposed_filter');
//console.log(form);
//console.log(form_state);
//console.log(element);
//console.log(filter);
//console.log(field);
var widget = filter.options.group_info.widget;
// List fields.
if (widget == 'select') {
// Set the element value if we have one in the filter.
if (!empty(filter.value)) { element.value = filter.value[0]; }
// Set the options, then depending on whether or not it is required, set
// the default value accordingly.
element.options = filter.value_options;
if (!element.required) {
element.options['All'] = '- ' + t('Any') + ' -';
if (typeof element.value === 'undefined') { element.value = 'All'; }
}
}
else {
console.log('WARNING: list_views_exposed_filter - unsupported widget:' + widget);
}
}
catch (error) { console.log('list_views_exposed_filter - ' + error); }
}
/**
* Implements hook_field_formatter_view().
* @param {String} entity_type
* @param {Object} entity
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Object} items
* @param {*} display
* @return {Object}
*/
function number_field_formatter_view(entity_type, entity, field, instance, langcode, items, display) {
try {
var element = {};
// If items is a string, convert it into a single item JSON object.
if (typeof items === 'string') {
items = {0: {value: items}};
}
if (!empty(items)) {
var prefix = '';
if (!empty(field.settings.prefix)) { prefix = field.settings.prefix; }
var suffix = '';
if (!empty(field.settings.suffix)) { suffix = field.settings.suffix; }
for (var delta in items) {
if (!items.hasOwnProperty(delta)) { continue; }
var item = items[delta];
element[delta] = {
markup: prefix + item.value + suffix
};
}
}
return element;
}
catch (error) { console.log('number_field_formatter_view - ' + error); }
}
/**
* Implements hook_field_widget_form().
* @param {Object} form
* @param {Object} form_state
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Object} items
* @param {Number} delta
* @param {Object} element
*/
function number_field_widget_form(form, form_state, field, instance, langcode, items, delta, element) {
try {
switch (element.type) {
case 'number_integer':
case 'number_float':
case 'number_decimal':
case 'range':
// Change the form element into a number, unless we're using a range
// slider. Then set its min/max attributes along with the step.
if (element.type != 'range') { items[delta].type = 'number'; }
if (!empty(instance.settings.max)) {
items[delta].options.attributes['min'] = instance.settings.min;
}
if (!empty(instance.settings.max)) {
items[delta].options.attributes['max'] = instance.settings.max;
}
var step = 1;
if (element.type == 'number_float') { step = 0.01; }
if (element.type == 'number_decimal') { step = 0.01; }
items[delta].options.attributes['step'] = step;
break;
default:
console.log(
'number_field_widget_form - element type not supported (' +
element.type +
')'
);
break;
}
}
catch (error) { console.log('number_field_widget_form - ' + error); }
}
/**
* Implements hook_field_widget_form().
* @param {Object} form
* @param {Object} form_state
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Object} items
* @param {Number} delta
* @param {Object} element
* @return {*}
*/
function options_field_widget_form(form, form_state, field, instance, langcode, items, delta, element) {
try {
switch (element.type) {
case 'checkbox':
// If the checkbox has a default value of 1, check the box.
if (items[delta].default_value == 1) { items[delta].checked = true; }
break;
case 'radios':
break;
case 'list_boolean':
switch (instance.widget.type) {
case 'options_onoff':
// Switch an on/off boolean to a checkbox and place its on/off
// values as attributes. Depending on the allowed values, we may
// have to iterate over an array, or an object to get the on/off
// values.
items[delta].type = 'checkbox';
var off = null;
var on = null;
if ($.isArray(field.settings.allowed_values)) {
for (var key in field.settings.allowed_values) {
if (off === null) { off = key; }
else { on = key; }
}
}
else {
for (var value in field.settings.allowed_values) {
if (!field.settings.allowed_values.hasOwnProperty(value)) { continue; }
var label = field.settings.allowed_values[value];
if (off === null) { off = value; }
else { on = value; }
}
}
items[delta].options.attributes['off'] = off;
items[delta].options.attributes['on'] = on;
// If the value equals the on value, then check the box.
if (
typeof items[delta] !== 'undefined' && items[delta].value == on
) { items[delta].options.attributes['checked'] = 'checked'; }
break;
default:
console.log(
'WARNING: options_field_widget_form list_boolean with ' +
'unsupported type (' + instance.widget.type + ')'
);
break;
}
break;
case 'select':
case 'list_text':
case 'list_float':
case 'list_integer':
if (instance) {
switch (instance.widget.type) {
case 'options_select':
items[delta].type = 'select';
// If the select list is required, add a 'Select' option and set
// it as the default. If it is optional, place a "none" option
// for the user to choose from.
var text = '- None -';
if (items[delta].required) {
text = '- ' + t('Select a value') + ' -';
}
items[delta].options[''] = text;
if (empty(items[delta].value)) { items[delta].value = ''; }
// If more than one value is allowed, turn it into a multiple
// select list.
if (field.cardinality != 1) {
items[delta].options.attributes['data-native-menu'] = 'false';
items[delta].options.attributes['multiple'] = 'multiple';
}
break;
case 'options_buttons':
// If there is one value allowed, we turn this into radio
// button(s), otherwise they will become checkboxes.
var type = 'checkboxes';
if (field.cardinality == 1) { type = 'radios'; }
items[delta].type = type;
break;
default:
console.log(
'WARNING: options_field_widget_form - unsupported widget (' +
instance.widget.type + ')'
);
return false;
break;
}
// If there are any allowed values, place them on the options
// list. Then check for a default value, and set it if necessary.
if (field && field.settings.allowed_values) {
for (var key in field.settings.allowed_values) {
if (!field.settings.allowed_values.hasOwnProperty(key)) { continue; }
var value = field.settings.allowed_values[key];
// Don't place values that are objects onto the options
// (i.e. commerce taxonomy term reference fields).
if (typeof value === 'object') { continue; }
// If the value already exists in the options, then someone
// else has populated the list (e.g. commerce), so don't do
// any processing.
if (typeof items[delta].options[key] !== 'undefined') {
break;
}
// Set the key and value for the option.
items[delta].options[key] = value;
}
if (instance.default_value && instance.default_value[delta] &&
typeof instance.default_value[delta].value !== 'undefined') {
items[delta].value = instance.default_value[delta].value;
if (items[delta].required) {
delete items[delta].options[''];
}
if (items[delta].item && typeof items[delta].item.value !== 'undefined') {
items[delta].value = items[delta].item.value;
}
}
}
}
break;
case 'taxonomy_term_reference':
// Change the item type to a hidden input.
items[delta].type = 'hidden';
// What vocabulary are we using?
var machine_name = field.settings.allowed_values[0].vocabulary;
var taxonomy_vocabulary =
taxonomy_vocabulary_machine_name_load(machine_name);
var widget_type = false;
if (instance.widget.type == 'options_select') {
widget_type = 'select';
}
else {
console.log(
'WARNING: options_field_widget_form() - ' + instance.widget.type +
' not yet supported for ' + element.type + ' form elements!'
);
return false;
}
var widget_id = items[delta].id + '-' + widget_type;
// If the select list is required, add a 'Select' option and set
// it as the default. If it is optional, place a "none" option
// for the user to choose from.
var text = '- ' + t('None') + ' -';
if (items[delta].required) {
text = '- ' + t('Select a value') + ' -';
}
items[delta].children.push({
type: widget_type,
attributes: {
id: widget_id,
onchange: "_theme_taxonomy_term_reference_onchange(this, '" +
items[delta].id +
"');"
},
options: { '': text }
});
// Attach a pageshow handler to the current page that will load the
// terms into the widget.
var options = {
'page_id': drupalgap_get_page_id(drupalgap_path_get()),
'jqm_page_event': 'pageshow',
'jqm_page_event_callback':
'_theme_taxonomy_term_reference_load_items',
'jqm_page_event_args': JSON.stringify({
'taxonomy_vocabulary': taxonomy_vocabulary,
'widget_id': widget_id
})
};
// Pass the field name so the page event handler can be called for
// each item.
items[delta].children.push({
markup: drupalgap_jqm_page_event_script_code(
options,
field.field_name
)
});
break;
default:
var msg = 'options_field_widget_form - unknown widget type: ' + element.type;
console.log(msg);
break;
}
}
catch (error) { console.log('options_field_widget_form - ' + error); }
}
/**
* Implements hook_field_formatter_view().
* @param {String} entity_type
* @param {Object} entity
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Object} items
* @param {*} display
* @return {Object}
*/
function text_field_formatter_view(entity_type, entity, field, instance, langcode, items, display) {
try {
var element = {};
if (items.length) {
for (var delta in items) {
if (!items.hasOwnProperty(delta)) { continue; }
// Grab the item, then grab the field value or its safe_value if we have it.
var item = items[delta];
var value = typeof item.safe_value !== 'undefined' ? item.safe_value : item.value;
// Any trim?
if (display.type == 'text_summary_or_trimmed') {
var length = display.settings.trim_length;
value = value.length > length ? value.substring(0, length - 3) + "..." : value.substring(0, length);
}
element[delta] = { markup: value };
}
}
return element;
}
catch (error) { console.log('text_field_formatter_view - ' + error); }
}
/**
* Implements hook_field_widget_form().
* @param {Object} form
* @param {Object} form_state
* @param {Object} field
* @param {Object} instance
* @param {String} langcode
* @param {Object} items
* @param {Number} delta
* @param {Object} element
*/
function text_field_widget_form(form, form_state, field, instance, langcode, items, delta, element) {
try {
// Determine the widget type, then set the delta item's type property.
var type = null;
switch (element.type) {
case 'search': type = 'search'; break;
case 'text': type = 'textfield'; break;
case 'textarea':
case 'text_long':
case 'text_with_summary':
case 'text_textarea':
type = 'textarea';
break;
}
items[delta].type = type;
// If the item has a value and its attribute value hasn't yet been set, then set the attribute value.
if (
typeof items[delta].value !== 'undefined' &&
typeof items[delta].options.attributes.value === 'undefined'
) { items[delta].options.attributes.value = items[delta].value; }
}
catch (error) { console.log('text_field_widget_form - ' + error); }
}