Moving loadStudy logic from Hanging Protocols to StudyMetadataSource
This commit is contained in:
1 parent
72bfbf69c7
commit
4fe9a2f9fc
4 files changed
+190
-76
No files matched your search
@@ -8,8 +8,9 @@ import 'meteor/ohif:viewerbase';
|
|||||||
* Import Constants
|
* Import Constants
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const StudyMetadata = OHIF.viewerbase.metadata.StudyMetadata;
|
const { OHIFError } = OHIF.viewerbase;
|
||||||
const StudySummary = OHIF.viewerbase.metadata.StudySummary;
|
const { StudyMetadata, StudySummary } = OHIF.viewerbase.metadata;
|
||||||
|
|
||||||
|
|
||||||
// Define a global variable that will be used to refer to the Protocol Engine
|
// Define a global variable that will be used to refer to the Protocol Engine
|
||||||
// It must be populated by HP.setEngine when the Viewer is initialized and a ProtocolEngine
|
// It must be populated by HP.setEngine when the Viewer is initialized and a ProtocolEngine
|
||||||
@@ -90,40 +91,50 @@ Meteor.startup(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log decisions regarding matching
|
/**
|
||||||
HP.match = function(attributes, rules) {
|
* Match a Metadata instance against rules using Validate.js for validation.
|
||||||
var options = {
|
* @param {StudyMetadata|SeriesMetadata|InstanceMetadata} metadataInstance Metadata instance object
|
||||||
|
* @param {Array} rules Array of MatchingRules instances (StudyMatchingRule|SeriesMatchingRule|ImageMatchingRule) for the match
|
||||||
|
* @return {Object} Matching Object with score and details (which rule passed or failed)
|
||||||
|
*/
|
||||||
|
HP.match = function(metadataInstance, rules) {
|
||||||
|
const options = {
|
||||||
format: 'grouped'
|
format: 'grouped'
|
||||||
};
|
};
|
||||||
|
|
||||||
var score = 0;
|
const details = {
|
||||||
var details = {
|
|
||||||
passed: [],
|
passed: [],
|
||||||
failed: []
|
failed: []
|
||||||
};
|
};
|
||||||
|
|
||||||
var requiredFailed = false;
|
let requiredFailed = false;
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
rules.forEach(rule => {
|
rules.forEach(rule => {
|
||||||
var attribute = rule.attribute;
|
const attribute = rule.attribute;
|
||||||
|
|
||||||
// If the attributes we are testing (e.g. study, series, or instance attributes) do
|
// If the metadataInstance we are testing (e.g. study, series, or instance MetadataInstance) do
|
||||||
// not contain the attribute specified in the rule, check whether or not they have been
|
// not contain the attribute specified in the rule, check whether or not they have been
|
||||||
// defined in the CustomAttributeRetrievalCallbacks Object.
|
// defined in the CustomAttributeRetrievalCallbacks Object.
|
||||||
|
if (!metadataInstance.customAttributeExists(attribute) &&
|
||||||
// TODO: Investigate why attributes.hasOwnProperty(attribute) doesn't work?
|
|
||||||
if (attributes[attribute] === undefined &&
|
|
||||||
HP.CustomAttributeRetrievalCallbacks.hasOwnProperty(attribute)) {
|
HP.CustomAttributeRetrievalCallbacks.hasOwnProperty(attribute)) {
|
||||||
var customAttribute = HP.CustomAttributeRetrievalCallbacks[attribute];
|
const customAttribute = HP.CustomAttributeRetrievalCallbacks[attribute];
|
||||||
attributes[attribute] = customAttribute.callback(attributes);
|
metadataInstance.setCustomAttribute(attribute, customAttribute.callback(metadataInstance));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the constraint as required by Validate.js
|
// Format the constraint as required by Validate.js
|
||||||
var testConstraint = {};
|
const testConstraint = {
|
||||||
testConstraint[attribute] = rule.constraint;
|
[attribute]: rule.constraint
|
||||||
|
};
|
||||||
|
|
||||||
// Use Validate.js to evaluate the constraints on the specified attributes
|
// Create a single attribute object to be validated, since metadataInstance is an
|
||||||
var errorMessages = validate(attributes, testConstraint, [options]);
|
// instance of Metadata (StudyMetadata, SeriesMetadata or InstanceMetadata)
|
||||||
|
const attributeMap = {
|
||||||
|
[attribute]: metadataInstance.getCustomAttribute(attribute)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use Validate.js to evaluate the constraints on the specified metadataInstance
|
||||||
|
const errorMessages = validate(attributeMap, testConstraint, [options]);
|
||||||
|
|
||||||
if (!errorMessages) {
|
if (!errorMessages) {
|
||||||
// If no errorMessages were returned, then validation passed.
|
// If no errorMessages were returned, then validation passed.
|
||||||
@@ -133,7 +144,7 @@ HP.match = function(attributes, rules) {
|
|||||||
|
|
||||||
// Log that this rule passed in the matching details object
|
// Log that this rule passed in the matching details object
|
||||||
details.passed.push({
|
details.passed.push({
|
||||||
rule: rule
|
rule
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// If errorMessages were present, then validation failed
|
// If errorMessages were present, then validation failed
|
||||||
@@ -147,8 +158,8 @@ HP.match = function(attributes, rules) {
|
|||||||
// Log that this rule failed in the matching details object
|
// Log that this rule failed in the matching details object
|
||||||
// and include any error messages
|
// and include any error messages
|
||||||
details.failed.push({
|
details.failed.push({
|
||||||
rule: rule,
|
rule,
|
||||||
errorMessages: errorMessages
|
errorMessages
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -159,13 +170,13 @@ HP.match = function(attributes, rules) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
score: score,
|
score,
|
||||||
details: details
|
details
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var sortByScore = function(arr) {
|
const sortByScore = arr => {
|
||||||
arr.sort(function(a, b) {
|
arr.sort((a, b) => {
|
||||||
return b.score - a.score;
|
return b.score - a.score;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -173,25 +184,28 @@ var sortByScore = function(arr) {
|
|||||||
HP.ProtocolEngine = class ProtocolEngine {
|
HP.ProtocolEngine = class ProtocolEngine {
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} LayoutManager Layout Manager Object
|
* @param {Object} layoutManager Layout Manager Object
|
||||||
* @param {Array} studies Array of study metadata
|
* @param {Array} studies Array of study metadata
|
||||||
* @param {Array} relatedStudies Array of related studies
|
* @param {Map} priorStudies Map of prior studies
|
||||||
* @param {Object} studyMedadataSource Instance of StudyMetadataSource (ohif-viewerbase) Object to get study metadata
|
* @param {Object} studyMedadataSource Instance of StudyMetadataSource (ohif-viewerbase) Object to get study metadata
|
||||||
*/
|
*/
|
||||||
constructor(layoutManager, studies, priorStudies, studyMetadataSource) {
|
constructor(layoutManager, studies, priorStudies, studyMetadataSource) {
|
||||||
|
|
||||||
|
const { LayoutManager, StudyMetadataSource } = OHIF.viewerbase;
|
||||||
// -----------
|
// -----------
|
||||||
// Validations
|
// Type Validations
|
||||||
|
|
||||||
if (!(layoutManager instanceof OHIF.viewerbase.LayoutManager)) {
|
if (!(layoutManager instanceof LayoutManager)) {
|
||||||
throw new OHIF.viewerbase.OHIFError('ProtocolEngine::constructor layoutManager is not an instance of LayoutManager');
|
throw new OHIFError('ProtocolEngine::constructor layoutManager is not an instance of LayoutManager');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(studyMetadataSource instanceof OHIF.viewerbase.StudyMetadataSource)) {
|
if (!(studyMetadataSource instanceof StudyMetadataSource)) {
|
||||||
throw new OHIF.viewerbase.OHIFError('ProtocolEngine::constructor studyMetadataSource is not an instance of StudyMetadataSource');
|
throw new OHIFError('ProtocolEngine::constructor studyMetadataSource is not an instance of StudyMetadataSource');
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: Validate "studies" parameter
|
if (!(studies instanceof Array) && !studies.every(study => study instanceof StudyMetadata)) {
|
||||||
|
throw new OHIFError('ProtocolEngine::constructor studies is not an array or it\'s items are not instances of StudyMetadata');
|
||||||
|
}
|
||||||
|
|
||||||
// --------------
|
// --------------
|
||||||
// Initialization
|
// Initialization
|
||||||
@@ -213,7 +227,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
* Resets the ProtocolEngine to the best match
|
* Resets the ProtocolEngine to the best match
|
||||||
*/
|
*/
|
||||||
reset() {
|
reset() {
|
||||||
var protocol = this.getBestMatch();
|
const protocol = this.getBestMatch();
|
||||||
this.setHangingProtocol(protocol);
|
this.setHangingProtocol(protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +254,10 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
study.numberOfPriorsReferenced = this.getNumberOfAvailablePriors(study.getStudyInstanceUID());
|
// Set custom attribute for study metadata
|
||||||
|
const numberOfPriorsReferenced = this.getNumberOfAvailablePriors(study.getStudyInstanceUID());
|
||||||
|
study.setCustomAttribute('numberOfPriorsReferenced', numberOfPriorsReferenced);
|
||||||
|
|
||||||
const rule = new HP.ProtocolMatchingRule('numberOfPriorsReferenced', {
|
const rule = new HP.ProtocolMatchingRule('numberOfPriorsReferenced', {
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: protocol.numberOfPriorsReferenced
|
greaterThanOrEqualTo: protocol.numberOfPriorsReferenced
|
||||||
@@ -356,9 +373,8 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
matchImages(viewport) {
|
matchImages(viewport) {
|
||||||
OHIF.log.info('ProtocolEngine::matchImages');
|
OHIF.log.info('ProtocolEngine::matchImages');
|
||||||
|
|
||||||
const studyMatchingRules = viewport.studyMatchingRules;
|
const { studyMatchingRules, seriesMatchingRules, imageMatchingRules: instanceMatchingRules } = viewport;
|
||||||
const seriesMatchingRules = viewport.seriesMatchingRules;
|
|
||||||
const instanceMatchingRules = viewport.imageMatchingRules;
|
|
||||||
const matchingScores = [];
|
const matchingScores = [];
|
||||||
const currentStudy = this.studies[0];
|
const currentStudy = this.studies[0];
|
||||||
|
|
||||||
@@ -367,7 +383,8 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
let highestImageMatchingScore = 0;
|
let highestImageMatchingScore = 0;
|
||||||
let bestMatch;
|
let bestMatch;
|
||||||
|
|
||||||
currentStudy.abstractPriorValue = 0;
|
// Set custom attribute for study metadata
|
||||||
|
currentStudy.setCustomAttribute('abstractPriorValue', 0);
|
||||||
|
|
||||||
studyMatchingRules.forEach(rule => {
|
studyMatchingRules.forEach(rule => {
|
||||||
if (rule.attribute === 'abstractPriorValue') {
|
if (rule.attribute === 'abstractPriorValue') {
|
||||||
@@ -391,41 +408,35 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
priorStudy = studies[studyIndex];
|
priorStudy = studies[studyIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invalid data
|
||||||
if (!(priorStudy instanceof StudyMetadata) && !(priorStudy instanceof StudySummary)) {
|
if (!(priorStudy instanceof StudyMetadata) && !(priorStudy instanceof StudySummary)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: Make sure this study is already loaded into the viewer (OHIF.viewer.Studies)
|
|
||||||
// @TypeSafeStudies
|
|
||||||
const priorStudyInstanceUID = priorStudy.getStudyInstanceUID();
|
const priorStudyInstanceUID = priorStudy.getStudyInstanceUID();
|
||||||
const alreadyLoaded = OHIF.viewer.Studies.findBy({
|
|
||||||
studyInstanceUid: priorStudyInstanceUID
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!alreadyLoaded) {
|
// Check if study metadata is already in studies list
|
||||||
this.studyMetadataSource.getByInstanceUID(priorStudyInstanceUID).then(study => {
|
if (this.studies.find(study => study.getStudyInstanceUID() === priorStudyInstanceUID)) {
|
||||||
study.abstractPriorValue = abstractPriorValue;
|
// Update the viewport to refresh layout manager with new study
|
||||||
|
this.updateViewports();
|
||||||
|
|
||||||
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
|
return;
|
||||||
const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
|
||||||
|
|
||||||
studyMetadata.setDisplaySets(displaySets);
|
|
||||||
|
|
||||||
study.selected = true;
|
|
||||||
study.displaySets = displaySets;
|
|
||||||
OHIF.viewer.Studies.insert(study);
|
|
||||||
OHIF.viewer.StudyMetadataList.insert(studyMetadata);
|
|
||||||
}, error => { console.alert(error) });
|
|
||||||
// this.studyMetadataSource.getByInstanceUID(priorStudy.studyInstanceUid).then(, study => {
|
|
||||||
// study.abstractPriorValue = abstractPriorValue;
|
|
||||||
// study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(study);
|
|
||||||
// OHIF.viewer.Studies.insert(study);
|
|
||||||
|
|
||||||
// this.studies.push(study);
|
|
||||||
// this.matchImages(viewport);
|
|
||||||
// this.updateViewports();
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get study metadata if necessary and load study in the viewer (each viewer should provide it's own load study method)
|
||||||
|
this.studyMetadataSource.loadStudy(priorStudy).then(studyMetadata => {
|
||||||
|
// Set the custom attribute abstractPriorValue for the study metadata
|
||||||
|
studyMetadata.setCustomAttribute('abstractPriorValue', abstractPriorValue);
|
||||||
|
|
||||||
|
// Insert the new study metadata
|
||||||
|
this.studies.push(studyMetadata);
|
||||||
|
|
||||||
|
// Re-match images
|
||||||
|
this.matchImages(viewport);
|
||||||
|
}, error => {
|
||||||
|
OHIF.log.warn(error);
|
||||||
|
throw new OHIFError(`ProtocolEngine::matchImages could not get study metadata for studyInstanceUID: ${priorStudyInstanceUID}`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// TODO: Add relative Date / time
|
// TODO: Add relative Date / time
|
||||||
});
|
});
|
||||||
@@ -527,8 +538,8 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
OHIF.log.info('ProtocolEngine::matchImages bestMatch', bestMatch);
|
OHIF.log.info('ProtocolEngine::matchImages bestMatch', bestMatch);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bestMatch: bestMatch,
|
bestMatch,
|
||||||
matchingScores: matchingScores
|
matchingScores
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,8 +669,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!currentViewportData.displaySetInstanceUid) {
|
if (!currentViewportData.displaySetInstanceUid) {
|
||||||
//@TODO: make OHIFError use Meteor.Error instead of simple Error()
|
throw new OHIFError('ProtocolEngine::updateViewports No matching display set found?');
|
||||||
throw new OHIF.viewerbase.OHIFError('ProtocolEngine::updateViewports No matching display set found?');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewportData.push(currentViewportData);
|
viewportData.push(currentViewportData);
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
|
import { Viewerbase } from 'meteor/ohif:viewerbase';
|
||||||
|
|
||||||
|
// TODO: [LT-refactor] move this to ohif:hanging-protocols package
|
||||||
|
/**
|
||||||
|
* Get a timepoint type for a given study metadata
|
||||||
|
* @param {StudyMetadata} study StudyMetadata instance
|
||||||
|
* @return {String|undefined} Timepoint type if found or undefined if not found or any error/missing information
|
||||||
|
*/
|
||||||
const getTimepointType = study => {
|
const getTimepointType = study => {
|
||||||
const instance = Template.instance();
|
const timepointApi = Template.instance().timepointApi;
|
||||||
const timepointApi = instance.timepointApi || instance.data.timepointApi;
|
|
||||||
if (!timepointApi) {
|
if (!timepointApi || !(study instanceof Viewerbase.metadata.StudyMetadata)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timepoint = timepointApi.study(study.studyInstanceUid)[0];
|
const timepoint = timepointApi.study(study.getStudyInstanceUID())[0];
|
||||||
if (!timepoint) {
|
if (!timepoint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import 'meteor/ohif:viewerbase';
|
import 'meteor/ohif:viewerbase';
|
||||||
|
|
||||||
|
// Important metadata classes
|
||||||
|
const { OHIFError, metadata } = OHIF.viewerbase;
|
||||||
|
const { StudySummary, StudyMetadata } = metadata;
|
||||||
|
|
||||||
export class OHIFStudyMetadataSource extends OHIF.viewerbase.StudyMetadataSource {
|
export class OHIFStudyMetadataSource extends OHIF.viewerbase.StudyMetadataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get study metadata for a study with given study InstanceUID
|
* Get study metadata for a study with given study InstanceUID
|
||||||
* @param {String} studyInstanceUID Study InstanceUID
|
* @param {String} studyInstanceUID Study InstanceUID
|
||||||
|
* @return {Promise} A Promise object
|
||||||
*/
|
*/
|
||||||
getByInstanceUID(studyInstanceUID) {
|
getByInstanceUID(studyInstanceUID) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -13,4 +18,60 @@ export class OHIFStudyMetadataSource extends OHIF.viewerbase.StudyMetadataSource
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load study info (OHIF.viewer.Studies) and study metadata (OHIF.viewer.StudyMetadataList) for a given study.
|
||||||
|
* @param {StudySummary|StudyMetadata} Instance of StudySummary or StudyMetadata object
|
||||||
|
*/
|
||||||
|
loadStudy(study) {
|
||||||
|
if (!(study instanceof StudyMetadata) && !(study instanceof StudySummary)) {
|
||||||
|
throw new OHIFError('OHIFStudyMetadataSource::loadStudy study is not an instance of StudySummary or StudyMetadata');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const studyInstanceUID = study.getStudyInstanceUID();
|
||||||
|
|
||||||
|
if (study instanceof StudyMetadata) {
|
||||||
|
const alreadyLoaded = OHIF.viewer.Studies.findBy({
|
||||||
|
studyInstanceUid: studyInstanceUID
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!alreadyLoaded) {
|
||||||
|
this._updateStudyCollections(study);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(study);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getByInstanceUID(study.getStudyInstanceUID()).then(studyInfo => {
|
||||||
|
// Create study metadata object
|
||||||
|
const studyMetadata = new OHIF.metadata.StudyMetadata(studyInfo);
|
||||||
|
|
||||||
|
// Get Study display sets
|
||||||
|
const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
||||||
|
|
||||||
|
// Set studyMetadata display sets
|
||||||
|
studyMetadata.setDisplaySets(displaySets);
|
||||||
|
|
||||||
|
this._updateStudyCollections(studyMetadata);
|
||||||
|
resolve(studyMetadata);
|
||||||
|
}, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static methods
|
||||||
|
static _updateStudyCollections(studyMetadata) {
|
||||||
|
const studyInfo = studyMetadata.getData();
|
||||||
|
|
||||||
|
// Set some studyInfo properties
|
||||||
|
studyInfo.selected = true;
|
||||||
|
studyInfo.displaySets = displaySets;
|
||||||
|
|
||||||
|
// Insert new study info object in Studies TypeSafeCollection
|
||||||
|
OHIF.viewer.Studies.insert(studyInfo);
|
||||||
|
|
||||||
|
// Insert new study metadata in StudyMetadataList TypeSafeCollection
|
||||||
|
OHIF.viewer.StudyMetadataList.insert(studyMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,15 @@ export class Metadata {
|
|||||||
writable: false,
|
writable: false,
|
||||||
value: data
|
value: data
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Define _custom properties as an immutable property
|
||||||
|
// IMPORTANT: This property can only be set during instance construction.
|
||||||
|
Object.defineProperty(this, '_custom', {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: false,
|
||||||
|
writable: false,
|
||||||
|
value: Object.create(null)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getData() {
|
getData() {
|
||||||
@@ -38,6 +47,33 @@ export class Metadata {
|
|||||||
return propertyValue;
|
return propertyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set custom attribute value
|
||||||
|
* @param {String} attribute Custom attribute name
|
||||||
|
* @param {Any} value Custom attribute value
|
||||||
|
*/
|
||||||
|
setCustomAttribute(attribute, value) {
|
||||||
|
this._custom[attribute] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get custom attribute value
|
||||||
|
* @param {String} attribute Custom attribute name
|
||||||
|
* @return {Any} Custom attribute value
|
||||||
|
*/
|
||||||
|
getCustomAttribute(attribute) {
|
||||||
|
return this._custom[attribute];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a custom attribute exists
|
||||||
|
* @param {String} attribute Custom attribute name
|
||||||
|
* @return {Boolean} True if custom attribute exists or false if not
|
||||||
|
*/
|
||||||
|
customAttributeExists(attribute) {
|
||||||
|
return attribute in this._custom;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static Methods
|
* Static Methods
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in new issue
Block a user