// console.log("Loading DataList.js");
var FieldDBObject = require("./../FieldDBObject").FieldDBObject;
var DocumentCollection = require("./../datum/DocumentCollection").DocumentCollection;
var Comments = require("./../comment/Comments").Comments;
var ContextualizableObject = require("./../locales/ContextualizableObject").ContextualizableObject;
var Q = require("q");
// console.log("Requireing FieldDBObject from datalist library ", FieldDBObject);
/**
* @class The Data List widget is used for import search, to prepare handouts and to share data on the web.
* @name DataList
*
* @description The Data List widget is used for import search, to prepare handouts and to share data on the web.
*
* @property {String} title The title of the Data List.
* @property {String} description The description of the Data List.
* @property {Array<String>} datumIds Deprecated: An ordered list of the datum IDs of the
* Datums in the Data List.
* @property {Array<String>} docIds An ordered list of the doc IDs of the
* Datums in the Data List.
* @property {Array<String>} docs An ordered collection of the doc IDs of the
* Datums in the Data List (not serialized in the DataList)
*
* @extends FieldDBObject
* @constructs
*/
var DataList = function DataList(options) {
if (!this._fieldDBtype) {
this._fieldDBtype = "DataList";
}
this.debug("Constructing DataList ", options);
if (options && options.comments) {
// this.debug("DataList comments", options.comments);
// this.debug("DataList comments", options.comments);
}
FieldDBObject.apply(this, arguments);
this.debug(" Constructed datalist ", this);
};
// console.log("DataList constructor", DataList);
DataList.prototype = Object.create(FieldDBObject.prototype, /** @lends DataList.prototype */ {
constructor: {
value: DataList
},
api: {
get: function() {
return this._api || FieldDBObject.DEFAULT_STRING;
},
set: function(value) {
this.ensureSetViaAppropriateType("api", value);
}
},
defaults: {
get: function() {
return {
title: "Untitled Data List",
description: "",
datumIds: FieldDBObject.DEFAULT_ARRAY,
docs: FieldDBObject.DEFAULT_COLLECTION
};
}
},
// Internal models: used by the parse function
INTERNAL_MODELS: {
value: {
comments: Comments,
docs: DocumentCollection,
title: ContextualizableObject,
description: ContextualizableObject
// item: FieldDBObject
}
},
title: {
get: function() {
return this._title || FieldDBObject.DEFAULT_STRING;
},
set: function(value) {
this.ensureSetViaAppropriateType("title", value);
}
},
description: {
get: function() {
return this._description || FieldDBObject.DEFAULT_STRING;
},
set: function(value) {
this.ensureSetViaAppropriateType("description", value);
}
},
docs: {
get: function() {
// if (this._docs && this._docs.primaryKey !== this.primaryKey) {
// this._docs.primaryKey = this.primaryKey;
// }
return this._docs;
},
set: function(value) {
this.debug("creating the " + this.id + " datalist's docs");
var previousPrimaryKey;
if (this._docs) {
previousPrimaryKey = this._docs.primaryKey;
}
this.ensureSetViaAppropriateType("docs", value);
if (this._docs) {
delete this._docIds;
if (previousPrimaryKey && previousPrimaryKey !== this._docs.primaryKey) {
this._docs.primaryKey = previousPrimaryKey;
}
}
}
},
add: {
value: function(value) {
if (value && Object.prototype.toString.call(value) === "[object Array]") {
var self = this;
for (var itemIndex in value) {
value[itemIndex] = self.add(value[itemIndex]);
}
return value;
}
if (!this.docs) {
this.debug("creating the datalist docs ", value);
this.docs = [];
} else {
this.debug("adding to existing datalist docs ", value);
}
//Item of the datalist trumps the collections' item type
if (this.INTERNAL_MODELS && this.INTERNAL_MODELS.item && !(value instanceof this.INTERNAL_MODELS.item)) {
value = new this.INTERNAL_MODELS.item(value);
} else {
this.debug("Setting the type of this new item to LanguageDatum since it didnt have a type before, and there is no default item for this datalist.");
value.fieldDBtype = value.fieldDBtype || "LanguageDatum";
}
return this.docs.add(value);
}
},
addReallyComplicatedDeprecated: {
value: function(value) {
if (!this.docs || this.docs.length === 0) {
if (Object.prototype.toString.call(value) === "[object Array]") {
value = {
collection: value,
// primaryKey: "hithere"
};
} else {
value = {
collection: [value]
};
}
value.primaryKey = this.primaryKey;
this.debug("setting the docs : ", value);
this.docs = value;
// this.docs.primaryKey = this.primaryKey;
return this.docs._collection[0];
}
this.debug("adding to existing data list", value);
return this.docs.add(value);
}
},
push: {
value: function(value) {
if (!this.docs) {
this.debug("creating the datalist docs ", value);
this.docs = [];
} else {
this.debug("adding to existing datalist docs ", value);
}
return this.docs.push(value);
}
},
unshift: {
value: function(value) {
if (!this.docs) {
this.debug("creating the datalist docs ", value);
this.docs = [];
} else {
this.debug("adding to existing datalist docs ", value);
}
return this.docs.unshift(value);
}
},
pop: {
value: function() {
if (!this.docs) {
return;
}
return this.docs.pop();
}
},
shift: {
value: function() {
if (!this.docs) {
return;
}
return this.docs.shift();
}
},
populate: {
value: function(results) {
var self = this;
if (!this.docs) {
this.debug("creating the datalist docs to populate ", this);
this.docs = [];
} else {
this.debug("populating an existing database ", this);
}
var guessedType;
this.save();
results = results.map(function(doc) {
// prevent recursion a bit
if (self.api !== "datalists") {
// doc.api = self.api;
}
doc.confidential = self.confidential;
// doc.url = self.url;
doc = FieldDBObject.convertDocIntoItsType(doc);
if (doc.fieldDBtype && doc.fieldDBtype === "LanguageDatum") {
guessedType = "LanguageDatum";
}
// self.debug("adding doc", doc);
// self.docs.add(doc); //This overwrites this doc if in the collection
return doc;
});
self.todo("OVERWRITING NEW INFO INTO EXISTING DATALIST, instead save the existing list, then add them and remove the ones that arent supposed ot be there.");
// self.docs.debugMode = true;
self.docs = results; //This ensures that the docs list matches what the server thinks.
// self.docs.merge("self", results); //This overwrites this doc if in the collection
if (guessedType === "LanguageDatum") {
self.showDocPosition = true;
self.showDocCheckboxes = true;
self.docsAreReorderable = true;
}
}
},
reindexFromApi: {
value: function() {
var self = this,
deferred = Q.defer();
this.whenReindexedFromApi = deferred.promise;
if (!this.api) {
this.debug("Not reindexing this datalist from the database, its not connected to an api.");
Q.nextTick(function() {
deferred.reject({
status: 412,
userFriendlyErrors: ["This datalist doesn't need to be re-indexed. 29834."]
});
});
return deferred.promise;
}
if (!this.corpus || typeof this.corpus.fetchCollection !== "function") {
self.fetching = self.loading = false;
self.warn("This datalist has no corpus, it doesnt know how to find out which data are in it.");
Q.nextTick(function() {
self.docs = self.docs || [];
deferred.reject({
status: 412,
userFriendlyErrors: ["This application has errored. Please notify its developers: Cannot reindex this datalist if the corpus is not specified."]
});
});
return deferred.promise;
}
// Q.nextTick(function() {
// deferred.resolve(self);
// });
var unableToFetchCurrentDataAffiliatedWithThisDataList = function(err) {
self.fetching = self.loading = false;
self.warn(" problem fetching the data list");
self.debug("err", err);
self.docs = self.docs || [];
deferred.reject(err);
// return self;
};
this.fetching = this.loading = true;
var dataListUrl = "/_design/data/_list/as_data_list/by_type?key=\"" + this.api + "\"";
this.corpus.fetchCollection(dataListUrl).then(function(generatedDatalist) {
self.fetching = self.loading = false;
self.debug("Downloaded the auto-genrated data list of datum ordered by creation date in this data list", generatedDatalist);
if (!generatedDatalist) {
self.bug("There was a problem downloading the list of data in the " + self.title + " data list. Please report this.");
return;
}
generatedDatalist.docIds = generatedDatalist.docIds || generatedDatalist.datumIds;
if (generatedDatalist.docIds && generatedDatalist.docIds.length > 0) {
// If the data list was empty, assign the docIds which will populate it with placeholders
if (!self.docs) {
self.docs = [];
}
self.debug("Iterating through the docids to make sure they are in the list.");
// If the data list doesn't have this id, add a placeholder
generatedDatalist.docIds.map(function(docPrimaryKey) {
self.debug("Looking at " + docPrimaryKey);
if (!self._docs[docPrimaryKey]) {
self.debug("converting " + docPrimaryKey + " into a placeholder");
var docPlaceholder = {
dbname: self.dbname,
loaded: false
};
docPlaceholder[self.primaryKey] = docPrimaryKey;
self.debug("adding " + docPrimaryKey + " into the docs");
self.add(docPlaceholder);
}
});
// If the generatedDatalist indicates this doc is (no longer) in this session, remove it?
if (self.length !== generatedDatalist.docIds.length) {
self.todo("Not removing items whcih the user currently has in this session which the server doesnt know about.");
}
delete generatedDatalist.docIds;
}
delete generatedDatalist.title;
delete generatedDatalist.description;
// self.merge("self", generatedDatalist);
self.render();
deferred.resolve(self);
// return self;
}, unableToFetchCurrentDataAffiliatedWithThisDataList)
.fail(function(error) {
console.error(error.stack, self);
deferred.reject(error);
})
.done(function() {
console.log("Done data list reindex if possible", self.docs.length);
});
return deferred.promise;
}
},
length: {
get: function() {
if (this.docIds) {
return this.docIds.length;
}
return 0;
},
set: function(value) {
this.warn("data list lengths comes from the size of the datumids, it cant be set manually to " + value);
}
},
primaryKey: {
get: function() {
if (this._docs && this._docs.primaryKey) {
return this._docs.primaryKey;
}
this.warn("cant get the primary key ", this._docs);
return "id";
},
set: function(value) {
if (this.docs && this.docs.primaryKey && this.docs.primaryKey !== value) {
this.docs.primaryKey = value;
}
}
},
docIds: {
get: function() {
var self = this;
if (this.docs && this.docs.length) {
this._docIds = [];
this._docIds = this.docs.map(function(doc) {
self.debug("geting doc id of this doc ", doc);
return doc[self.primaryKey];
});
}
return this._docIds || FieldDBObject.DEFAULT_ARRAY;
},
set: function(value) {
if (value === this._docIds) {
return;
}
if (!value || value === []) {
// delete this._docIds;
return;
}
if (typeof value.map !== "function") {
console.error(" trying to set docIds of datalist to something that isnt an array.", value);
throw new Error("This is a very odd set of docIds " + value);
}
var self = this;
if (!this._docs || !this._docs.add || typeof this._docs.add !== "function") {
this.docs = [];
}
value.map(function(docPrimaryKey) {
self.debug("Looking at " + docPrimaryKey);
if (self._docs && !self._docs[docPrimaryKey]) {
self.debug("converting " + docPrimaryKey + " into a placeholder");
var docPlaceholder = {
dbname: self.dbname,
loaded: false
};
docPlaceholder[self.primaryKey] = docPrimaryKey;
self.debug("adding " + docPrimaryKey + " into the docs");
self.add(docPlaceholder);
}
});
}
},
datumIds: {
get: function() {
this.warn("datumIds is deprecated, please use docIds instead.");
return this.docIds;
},
set: function(value) {
this.warn("datumIds is deprecated, please use docIds instead.");
this.docIds = value;
}
},
icon: {
get: function() {
return "thumb-tack";
}
},
getAllAudioAndVideoFiles: {
value: function(datumIdsToGetAudioVideo) {
var deferred = Q.defer(),
self = this;
Q.nextTick(function() {
if (!datumIdsToGetAudioVideo) {
datumIdsToGetAudioVideo = self.docIds;
}
if (datumIdsToGetAudioVideo.length === 0) {
datumIdsToGetAudioVideo = self.docIds;
}
var audioVideoFiles = [];
self.debug("DATA LIST datumIdsToGetAudioVideo " + JSON.stringify(datumIdsToGetAudioVideo));
datumIdsToGetAudioVideo.map(function(id) {
var doc = self.docs[id];
if (!doc) {
return;
}
var audioVideo = doc.audioVideo || doc._audioVideo;
if (!audioVideo || typeof audioVideo.map !== "function") {
return;
}
audioVideo.map(function(audioVideoFile) {
if (audioVideoFile && audioVideoFile.URL) {
audioVideoFiles.push(audioVideoFile.URL);
}
});
});
deferred.resolve(audioVideoFiles);
});
return deferred.promise;
}
},
select: {
value: function(attributeToTriggerSelection) {
if (!this.docs || !this.docs.collection || typeof this.docs.collection.map !== "function") {
return [];
}
var selected = [],
self = this;
if (attributeToTriggerSelection && attributeToTriggerSelection === "all") {
attributeToTriggerSelection = this.primaryKey;
}
this.docs.collection.map(function(item) {
if (!attributeToTriggerSelection && item.selected) {
item.selected = true;
selected.push(item[self.primaryKey]);
return;
}
if (attributeToTriggerSelection && item[attributeToTriggerSelection]) {
item.selected = true;
selected.push(item[self.primaryKey]);
return;
}
item.selected = false;
});
this.debug("Selected ", selected);
return selected;
}
},
applyFunctionToAllIds: {
value: function(docIdsToApplyFunction, functionToApply, functionArguments) {
if (!docIdsToApplyFunction) {
docIdsToApplyFunction = this.docIds;
}
if (docIdsToApplyFunction.length === 0) {
docIdsToApplyFunction = this.select();
}
if (!functionToApply) {
functionToApply = "latexitDataList";
}
if (!functionArguments) {
// functionArguments = true; //leave it null so that the defualts will apply in the Datum call
}
var self = this;
this.debug("DATA LIST docIdsToApplyFunction " + JSON.stringify(docIdsToApplyFunction));
docIdsToApplyFunction.map(function(id) {
var doc = self.docs[id];
if (doc) {
doc[functionToApply].apply(doc, functionArguments);
return id;
} else {
self.warn(" Doc has not been fetched, cant apply the function to it.");
return id;
}
});
}
},
toJSON: {
value: function(includeEvenEmptyAttributes, removeEmptyAttributes, attributesToIgnore) {
// Force docIds to be set to current docs
if (this.docs && this.docs.length > 0) {
this.docIds = null;
this.docIds = this.docIds;
}
attributesToIgnore = attributesToIgnore || [];
attributesToIgnore.push("_docs");
this.debug("Customizing toJSON ", includeEvenEmptyAttributes, attributesToIgnore);
var json = FieldDBObject.prototype.toJSON.apply(this, [includeEvenEmptyAttributes, removeEmptyAttributes, attributesToIgnore]);
// delete json.docs;
this.todo("Adding datumIds for backward compatability until prototype can handle docIds");
json.datumIds = this.docIds;
this.debug(json);
return json;
}
}
});
exports.DataList = DataList;
// console.log("Exported DataList", exports);