UNPKG

prismic.io

Version:

JavaScript development kit for prismic.io

1,681 lines (1,508 loc) 356 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function (global){ 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var Requests = require('./requests'), Cookies = require('./cookies'), documents = require('./documents'), ApiCache = require('./cache'), Predicates = require('./predicates'), experiments = require('./experiments'); var Experiments = experiments.Experiments, Document = documents.Document; var experimentCookie = "io.prismic.experiment", previewCookie = "io.prismic.preview"; /** * Initialisation of the API object. * This is for internal use, from outside this kit, you should call Prismic.Api() * @private */ function Api(url, options) { var opts = options || {}; this.accessToken = opts.accessToken; this.url = url + (this.accessToken ? (url.indexOf('?') > -1 ? '&' : '?') + 'access_token=' + this.accessToken : ''); this.req = opts.req; this.apiCache = opts.apiCache || globalCache(); this.requestHandler = opts.requestHandler || Requests.request; this.apiCacheKey = this.url + (this.accessToken ? '#' + this.accessToken : ''); this.apiDataTTL = opts.apiDataTTL || 5; return this; } Api.prototype = { // Predicates AT: "at", ANY: "any", SIMILAR: "similar", FULLTEXT: "fulltext", NUMBER: { GT: "number.gt", LT: "number.lt" }, DATE: { // Other date operators are available: see the documentation. AFTER: "date.after", BEFORE: "date.before", BETWEEN: "date.between" }, // Fragment: usable as the second element of a query array on most predicates (except SIMILAR). // You can also use "my.*" for your custom fields. DOCUMENT: { ID: "document.id", TYPE: "document.type", TAGS: "document.tags" }, data: null, /** * Fetches data used to construct the api client, from cache if it's * present, otherwise from calling the prismic api endpoint (which is * then cached). * * @param {function} callback - Callback to receive the data. Optional, you can use the promise result. * @returns {Promise} Promise holding the data or error */ get: function get(callback) { var self = this; var cacheKey = this.apiCacheKey; return new Promise(function (resolve, reject) { var cb = function cb(err, value, xhr, ttl) { if (callback) callback(err, value, xhr, ttl); if (value) resolve(value); if (err) reject(err); }; self.apiCache.get(cacheKey, function (err, value) { if (err || value) { cb(err, value); return; } self.requestHandler(self.url, function (err, data, xhr, ttl) { if (err) { cb(err, null, xhr, ttl); return; } var parsed = self.parse(data); ttl = ttl || self.apiDataTTL; self.apiCache.set(cacheKey, parsed, ttl, function (err) { cb(err, parsed, xhr, ttl); }); }); }); }); }, /** * Cleans api data from the cache and fetches an up to date copy. * * @param {function} callback - Optional callback function that is called after the data has been refreshed * @returns {Promise} */ refresh: function refresh(callback) { var self = this; var cacheKey = this.apiCacheKey; return new Promise(function (resolve, reject) { var cb = function cb(err, value, xhr) { if (callback) callback(err, value, xhr); if (value) resolve(value); if (err) reject(err); }; self.apiCache.remove(cacheKey, function (err) { if (err) { cb(err);return; } self.get(function (err, data) { if (err) { cb(err);return; } self.data = data; self.bookmarks = data.bookmarks; self.experiments = new Experiments(data.experiments); cb(); }); }); }); }, /** * Parses and returns the /api document. * This is for internal use, from outside this kit, you should call Prismic.Api() * * @param {string} data - The JSON document responded on the API's endpoint * @returns {Api} - The Api object that can be manipulated * @private */ parse: function parse(data) { var refs, master, forms = {}, form, types, tags, f, i; // Parse the forms for (i in data.forms) { if (data.forms.hasOwnProperty(i)) { f = data.forms[i]; if (this.accessToken) { f.fields['access_token'] = {}; f.fields['access_token']['type'] = 'string'; f.fields['access_token']['default'] = this.accessToken; } form = new Form(f.name, f.fields, f.form_method, f.rel, f.enctype, f.action); forms[i] = form; } } refs = data.refs.map(function (r) { return new Ref(r.ref, r.label, r.isMasterRef, r.scheduledAt, r.id); }) || []; master = refs.filter(function (r) { return r.isMaster === true; }); types = data.types; tags = data.tags; if (master.length === 0) { throw "No master ref."; } return { bookmarks: data.bookmarks || {}, refs: refs, forms: forms, master: master[0], types: types, tags: tags, experiments: data.experiments, oauthInitiate: data['oauth_initiate'], oauthToken: data['oauth_token'], quickRoutes: data.quickRoutes }; }, /** * @deprecated use form() now * @param {string} formId - The id of a form, like "everything", or "products" * @returns {SearchForm} - the SearchForm that can be used. */ forms: function forms(formId) { return this.form(formId); }, /** * Returns a useable form from its id, as described in the RESTful description of the API. * For instance: api.form("everything") works on every repository (as "everything" exists by default) * You can then chain the calls: api.form("everything").query('[[:d = at(document.id, "UkL0gMuvzYUANCpf")]]').ref(ref).submit() * * @param {string} formId - The id of a form, like "everything", or "products" * @returns {SearchForm} - the SearchForm that can be used. */ form: function form(formId) { var form = this.data.forms[formId]; if (form) { return new SearchForm(this, form, {}); } return null; }, /** * The ID of the master ref on this prismic.io API. * Do not use like this: searchForm.ref(api.master()). * Instead, set your ref once in a variable, and call it when you need it; this will allow to change the ref you're viewing easily for your entire page. * * @returns {string} */ master: function master() { return this.data.master.ref; }, /** * Returns the ref ID for a given ref's label. * Do not use like this: searchForm.ref(api.ref("Future release label")). * Instead, set your ref once in a variable, and call it when you need it; this will allow to change the ref you're viewing easily for your entire page. * * @param {string} label - the ref's label * @returns {string} */ ref: function ref(label) { for (var i = 0; i < this.data.refs.length; i++) { if (this.data.refs[i].label == label) { return this.data.refs[i].ref; } } return null; }, /** * The current experiment, or null * @returns {Experiment} */ currentExperiment: function currentExperiment() { return this.experiments.current(); }, quickRoutesEnabled: function quickRoutesEnabled() { return this.data.quickRoutes.enabled; }, /** * Retrieve quick routes definitions */ quickRoutes: function quickRoutes(callback) { var self = this; return new Promise(function (resolve, reject) { self.requestHandler(self.data.quickRoutes.url, function (err, data, xhr) { if (callback) callback(err, data, xhr); if (err) reject(err); if (data) resolve(data); }); }); }, /** * Query the repository * @param {string|array|Predicate} the query itself * @param {object} additional parameters. In NodeJS, pass the request as 'req'. * @param {function} callback(err, response) */ query: function query(q, options, callback) { if (typeof options === 'function') { callback = options; options = undefined; } var opts = options || {}; var form = this.form('everything'); for (var key in opts) { form = form.set(key, options[key]); } // Don't override the ref if the caller specified one in the options if (!opts['ref']) { // Look in cookies if we have a ref (preview or experiment) var cookieString = ''; if (this.req) { // NodeJS cookieString = this.req.headers["cookie"] || ''; } else if (typeof window !== 'undefined') { // Browser cookieString = window.document.cookie || ''; } var cookies = Cookies.parse(cookieString); var previewRef = cookies[previewCookie]; var experimentRef = this.experiments.refFromCookie(cookies[experimentCookie]); form = form.ref(previewRef || experimentRef || this.master()); } if (q) { form.query(q); } return form.submit(callback); }, /** * Retrieve the document returned by the given query * @param {string|array|Predicate} the query * @param {object} additional parameters. In NodeJS, pass the request as 'req'. * @param {function} callback(err, doc) */ queryFirst: function queryFirst(q, options, callback) { if (typeof options === 'function') { callback = options; options = undefined; } var opts = {}; for (var key in options || {}) { opts[key] = options[key]; } opts.page = 1; opts.pageSize = 1; return this.query(q, opts, function (err, response) { if (callback) { var result = response && response.results && response.results[0]; callback(err, result); } }).then(function (response) { return response && response.results && response.results[0]; }); }, /** * Retrieve the document with the given id * @param {string} id * @param {object} additional parameters * @param {function} callback(err, doc) */ getByID: function getByID(id, options, callback) { options = options || {}; if (!options.lang) options.lang = '*'; return this.queryFirst(Predicates.at('document.id', id), options, callback); }, /** * Retrieve multiple documents from an array of id * @param {array} ids * @param {object} additional parameters * @param {function} callback(err, response) */ getByIDs: function getByIDs(ids, options, callback) { options = options || {}; if (!options.lang) options.lang = '*'; return this.query(['in', 'document.id', ids], options, callback); }, /** * Retrieve the document with the given uid * @param {string} type the custom type of the document * @param {string} uid * @param {object} additional parameters * @param {function} callback(err, response) */ getByUID: function getByUID(type, uid, options, callback) { options = options || {}; if (!options.lang) options.lang = '*'; return this.queryFirst(Predicates.at('my.' + type + '.uid', uid), options, callback); }, /** * Retrieve the singleton document with the given type * @param {string} type the custom type of the document * @param {object} additional parameters * @param {function} callback(err, response) */ getSingle: function getSingle(type, options, callback) { return this.queryFirst(Predicates.at('document.type', type), options, callback); }, /** * Retrieve the document with the given bookmark * @param {string} bookmark name * @param {object} additional parameters * @param {function} callback(err, response) * @returns {Promise} */ getBookmark: function getBookmark(bookmark, options, callback) { return new Promise(function (resolve, reject) { var id = this.bookmarks[bookmark]; if (id) { resolve(id); } else { var err = new Error("Error retrieving bookmarked id"); if (callback) callback(err); reject(err); } }).then(function (id) { return this.getByID(id, options, callback); }); }, /** * Return the URL to display a given preview * @param {string} token as received from Prismic server to identify the content to preview * @param {function} linkResolver the link resolver to build URL for your site * @param {string} defaultUrl the URL to default to return if the preview doesn't correspond to a document * (usually the home page of your site) * @param {function} callback to get the resulting URL (optional, you can get it from the Promise result) * @returns {Promise} */ previewSession: function previewSession(token, linkResolver, defaultUrl, callback) { var api = this; return new Promise(function (resolve, reject) { var cb = function cb(err, value, xhr) { if (callback) callback(err, value, xhr); if (err) { reject(err); } else { resolve(value); } }; api.requestHandler(token, function (err, result, xhr) { if (err) { cb(err, defaultUrl, xhr); return; } try { var mainDocumentId = result.mainDocument; if (!mainDocumentId) { cb(null, defaultUrl, xhr); } else { api.form("everything").query(Predicates.at("document.id", mainDocumentId)).ref(token).lang('*').submit(function (err, response) { if (err) { cb(err); } try { if (response.results.length === 0) { cb(null, defaultUrl, xhr); } else { cb(null, linkResolver(response.results[0]), xhr); } } catch (e) { cb(e); } }); } } catch (e) { cb(e, defaultUrl, xhr); } }); }); }, /** * Fetch a URL corresponding to a query, and parse the response as a Response object */ request: function request(url, callback) { var api = this; var cacheKey = url + (this.accessToken ? '#' + this.accessToken : ''); var cache = this.apiCache; function run(cb) { cache.get(cacheKey, function (err, value) { if (err || value) { cb(err, api.response(value)); return; } api.requestHandler(url, function (err, documents, xhr, ttl) { if (err) { cb(err, null, xhr); return; } if (ttl) { cache.set(cacheKey, documents, ttl, function (err) { cb(err, api.response(documents)); }); } else { cb(null, api.response(documents)); } }); }); } return new Promise(function (resolve, reject) { run(function (err, value, xhr) { if (callback) callback(err, value, xhr); if (err) reject(err); if (value) resolve(value); }); }); }, getNextPage: function getNextPage(nextPage, callback) { return this.request(nextPage + (this.accessToken ? '&access_token=' + this.accessToken : ''), callback); }, /** * JSON documents to Response object */ response: function response(documents) { var results = documents.results.map(parseDoc); return new Response(documents.page, documents.results_per_page, documents.results_size, documents.total_results_size, documents.total_pages, documents.next_page, documents.prev_page, results || []); } }; /** * Embodies a submittable RESTful form as described on the API endpoint (as per RESTful standards) * @constructor * @private */ function Form(name, fields, form_method, rel, enctype, action) { this.name = name; this.fields = fields; this.form_method = form_method; this.rel = rel; this.enctype = enctype; this.action = action; } Form.prototype = {}; /** * Parse json as a document * * @returns {Document} */ var parseDoc = function parseDoc(json) { var fragments = {}; for (var field in json.data[json.type]) { fragments[json.type + '.' + field] = json.data[json.type][field]; } var slugs = []; if (json.slugs !== undefined) { for (var i = 0; i < json.slugs.length; i++) { slugs.push(decodeURIComponent(json.slugs[i])); } } return new Document(json.id, json.uid || null, json.type, json.href, json.tags, slugs, json.first_publication_date, json.last_publication_date, json.lang, json.alternate_languages, fragments, json.data); }; /** * Embodies a SearchForm object. To create SearchForm objects that are allowed in the API, please use the API.form() method. * @constructor * @global * @alias SearchForm */ function SearchForm(api, form, data) { this.api = api; this.form = form; this.data = data || {}; for (var field in form.fields) { if (form.fields[field]['default']) { this.data[field] = [form.fields[field]['default']]; } } } SearchForm.prototype = { /** * Set an API call parameter. This will only work if field is a valid field of the * RESTful form in the first place (as described in the /api document); otherwise, * an "Unknown field" error is thrown. * Please prefer using dedicated methods like query(), orderings(), ... * * @param {string} field - The name of the field to set * @param {string} value - The value that gets assigned * @returns {SearchForm} - The SearchForm itself */ set: function set(field, value) { var fieldDesc = this.form.fields[field]; if (!fieldDesc) throw new Error("Unknown field " + field); var values = this.data[field] || []; if (value === '' || value === undefined) { // we must compare value to null because we want to allow 0 value = null; } if (fieldDesc.multiple) { if (value) values.push(value); } else { values = value && [value]; } this.data[field] = values; return this; }, /** * Sets a ref to query on for this SearchForm. This is a mandatory * method to call before calling submit(), and api.form('everything').submit() * will not work. * * @param {Ref} ref - The Ref object defining the ref to query * @returns {SearchForm} - The SearchForm itself */ ref: function ref(_ref) { return this.set("ref", _ref); }, /** * Sets a predicate-based query for this SearchForm. This is where you * paste what you compose in your prismic.io API browser. * * @example form.query(Prismic.Predicates.at("document.id", "foobar")) * @param {string|...array} query - Either a query as a string, or as many predicates as you want. See Prismic.Predicates. * @returns {SearchForm} - The SearchForm itself */ query: function query(_query) { if (typeof _query === 'string') { return this.set("q", _query); } else { var predicates; if (_query.constructor === Array && _query.length > 0 && _query[0].constructor === Array) { predicates = _query; } else { predicates = [].slice.apply(arguments); // Convert to a real JS array } var stringQueries = []; predicates.forEach(function (predicate) { stringQueries.push(Predicates.toQuery(predicate)); }); return this.query("[" + stringQueries.join("") + "]"); } }, /** * Sets a page size to query for this SearchForm. This is an optional method. * * @param {number} size - The page size * @returns {SearchForm} - The SearchForm itself */ pageSize: function pageSize(size) { return this.set("pageSize", size); }, /** * Restrict the results document to the specified fields * * @param {string|array} fields - The list of fields, array or comma separated string * @returns {SearchForm} - The SearchForm itself */ fetch: function fetch(fields) { if (fields instanceof Array) { fields = fields.join(","); } return this.set("fetch", fields); }, /** * Include the requested fields in the DocumentLink instances in the result * * @param {string|array} fields - The list of fields, array or comma separated string * @returns {SearchForm} - The SearchForm itself */ fetchLinks: function fetchLinks(fields) { if (fields instanceof Array) { fields = fields.join(","); } return this.set("fetchLinks", fields); }, /** * Sets the language to query for this SearchForm. This is an optional method. * * @param {string} fields - The language code * @returns {SearchForm} - The SearchForm itself */ lang: function lang(fields) { return this.set("lang", fields); }, /** * Sets the page number to query for this SearchForm. This is an optional method. * * @param {number} p - The page number * @returns {SearchForm} - The SearchForm itself */ page: function page(p) { return this.set("page", p); }, /** * Sets the orderings to query for this SearchForm. This is an optional method. * * @param {array} orderings - Array of string: list of fields, optionally followed by space and desc. Example: ['my.product.price desc', 'my.product.date'] * @returns {SearchForm} - The SearchForm itself */ orderings: function orderings(_orderings) { if (typeof _orderings === 'string') { // Backward compatibility return this.set("orderings", _orderings); } else if (!_orderings) { // Noop return this; } else { // Normal usage return this.set("orderings", "[" + _orderings.join(",") + "]"); } }, /** * Submits the query, and calls the callback function. * * @param {function} callback - Optional callback function that is called after the query was made, * to which you may pass three parameters: a potential error (null if no problem), * a Response object (containing all the pagination specifics + the array of Docs), * and the XMLHttpRequest */ submit: function submit(callback) { var self = this; var url = this.form.action; if (this.data) { var sep = url.indexOf('?') > -1 ? '&' : '?'; for (var key in this.data) { if (this.data.hasOwnProperty(key)) { var values = this.data[key]; if (values) { for (var i = 0; i < values.length; i++) { url += sep + key + '=' + encodeURIComponent(values[i]); sep = '&'; } } } } } return self.api.request(url, callback); } }; /** * Embodies the response of a SearchForm query as returned by the API. * It includes all the fields that are useful for pagination (page, total_pages, total_results_size, ...), * as well as the field "results", which is an array of {@link Document} objects, the documents themselves. * * @constructor * @global */ function Response(page, results_per_page, results_size, total_results_size, total_pages, next_page, prev_page, results) { /** * The current page * @type {number} */ this.page = page; /** * The number of results per page * @type {number} */ this.results_per_page = results_per_page; /** * The size of the current page * @type {number} */ this.results_size = results_size; /** * The total size of results across all pages * @type {number} */ this.total_results_size = total_results_size; /** * The total number of pages * @type {number} */ this.total_pages = total_pages; /** * The URL of the next page in the API * @type {string} */ this.next_page = next_page; /** * The URL of the previous page in the API * @type {string} */ this.prev_page = prev_page; /** * Array of {@link Document} for the current page * @type {Array} */ this.results = results; } /** * Embodies a prismic.io ref (a past or future point in time you can query) * @constructor * @global */ function Ref(ref, label, isMaster, scheduledAt, id) { /** * @field * @description the ID of the ref */ this.ref = ref; /** * @field * @description the label of the ref */ this.label = label; /** * @field * @description is true if the ref is the master ref */ this.isMaster = isMaster; /** * @field * @description the scheduled date of the ref */ this.scheduledAt = scheduledAt; /** * @field * @description the name of the ref */ this.id = id; } Ref.prototype = {}; function globalCache() { var g; if ((typeof global === 'undefined' ? 'undefined' : _typeof(global)) == 'object') { g = global; // NodeJS } else { g = window; // browser } if (!g.prismicCache) { g.prismicCache = new ApiCache(); } return g.prismicCache; } module.exports = { experimentCookie: experimentCookie, previewCookie: previewCookie, Api: Api, Form: Form, SearchForm: SearchForm, Ref: Ref, parseDoc: parseDoc }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./cache":3,"./cookies":4,"./documents":5,"./experiments":6,"./predicates":9,"./requests":12}],2:[function(require,module,exports){ 'use strict'; // IE below 12 doesn't support promises var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; require('es6-promise').polyfill(); // Polyfill for inheritance if (typeof Object.create != 'function') { Object.create = function () { var Object = function Object() {}; return function (prototype) { if (arguments.length > 1) { throw Error('Second argument not supported'); } if ((typeof prototype === 'undefined' ? 'undefined' : _typeof(prototype)) != 'object') { throw TypeError('Argument must be an object'); } Object.prototype = prototype; var result = {}; Object.prototype = null; return result; }; }(); } window.Prismic = require('./prismic'); },{"./prismic":10,"es6-promise":20}],3:[function(require,module,exports){ "use strict"; var LRUCache = require('./lru'); /** * Api cache */ function ApiCache(limit) { this.lru = new LRUCache(limit); } ApiCache.prototype = { get: function get(key, cb) { var maybeEntry = this.lru.get(key); if (maybeEntry && !this.isExpired(key)) { return cb(null, maybeEntry.data); } return cb(); }, set: function set(key, value, ttl, cb) { this.lru.remove(key); this.lru.put(key, { data: value, expiredIn: ttl ? Date.now() + ttl * 1000 : 0 }); return cb(); }, isExpired: function isExpired(key) { var entry = this.lru.get(key); if (entry) { return entry.expiredIn !== 0 && entry.expiredIn < Date.now(); } else { return false; } }, remove: function remove(key, cb) { this.lru.remove(key); return cb(); }, clear: function clear(cb) { this.lru.removeAll(); return cb(); } }; module.exports = ApiCache; },{"./lru":8}],4:[function(require,module,exports){ "use strict"; // Some portions of code from https://github.com/jshttp/cookie var decode = decodeURIComponent; function tryDecode(str, decode) { try { return decode(str); } catch (e) { return str; } } function parse(str, options) { if (typeof str !== 'string') { throw new TypeError('argument str must be a string'); } var obj = {}; var opt = options || {}; var pairs = str.split(/; */); var dec = opt.decode || decode; pairs.forEach(function (pair) { var eq_idx = pair.indexOf('='); // skip things that don't look like key=value if (eq_idx < 0) { return; } var key = pair.substr(0, eq_idx).trim(); var val = pair.substr(++eq_idx, pair.length).trim(); // quoted values if ('"' == val[0]) { val = val.slice(1, -1); } // only assign once if (undefined == obj[key]) { obj[key] = tryDecode(val, dec); } }); return obj; } module.exports = { parse: parse }; },{}],5:[function(require,module,exports){ "use strict"; var DateUtils = require('./utils/date'); /** * Functions to access fragments: superclass for Document and Doc (from Group), not supposed to be created directly * @constructor */ function WithFragments() {} WithFragments.prototype = { /** * Gets the fragment in the current Document object. Since you most likely know the type * of this fragment, it is advised that you use a dedicated method, like get StructuredText() or getDate(), * for instance. * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.author" * @returns {object} - The JavaScript Fragment object to manipulate */ get: function get(name) { var frags = this._getFragments(name); return frags.length ? frags[0] : null; }, /** * Builds an array of all the fragments in case they are multiple. * * @param {string} name - The name of the multiple fragment to get, with its type; for instance, "blog-post.author" * @returns {array} - An array of each JavaScript fragment object to manipulate. */ getAll: function getAll(name) { return this._getFragments(name); }, /** * Gets the image fragment in the current Document object, for further manipulation. * * @example document.getImage('blog-post.photo').asHtml(linkResolver) * * @param {string} fragment - The name of the fragment to get, with its type; for instance, "blog-post.photo" * @returns {ImageEl} - The Image object to manipulate */ getImage: function getImage(fragment) { var Fragments = require('./fragments'); var img = this.get(fragment); if (img instanceof Fragments.Image) { return img; } if (img instanceof Fragments.StructuredText) { // find first image in st. return img; } return null; }, // Useful for obsolete multiples getAllImages: function getAllImages(fragment) { var Fragments = require('./fragments'); var images = this.getAll(fragment); return images.map(function (image) { if (image instanceof Fragments.Image) { return image; } if (image instanceof Fragments.StructuredText) { throw new Error("Not done."); } return null; }); }, getFirstImage: function getFirstImage() { var Fragments = require('./fragments'); var fragments = this.fragments; var firstImage = Object.keys(fragments).reduce(function (image, key) { if (image) { return image; } else { var element = fragments[key]; if (typeof element.getFirstImage === "function") { return element.getFirstImage(); } else if (element instanceof Fragments.Image) { return element; } else return null; } }, null); return firstImage; }, getFirstTitle: function getFirstTitle() { var Fragments = require('./fragments'); var fragments = this.fragments; var firstTitle = Object.keys(fragments).reduce(function (st, key) { if (st) { return st; } else { var element = fragments[key]; if (typeof element.getFirstTitle === "function") { return element.getFirstTitle(); } else if (element instanceof Fragments.StructuredText) { return element.getTitle(); } else return null; } }, null); return firstTitle; }, getFirstParagraph: function getFirstParagraph() { var fragments = this.fragments; var firstParagraph = Object.keys(fragments).reduce(function (st, key) { if (st) { return st; } else { var element = fragments[key]; if (typeof element.getFirstParagraph === "function") { return element.getFirstParagraph(); } else return null; } }, null); return firstParagraph; }, /** * Gets the view within the image fragment in the current Document object, for further manipulation. * * @example document.getImageView('blog-post.photo', 'large').asHtml(linkResolver) * * @param {string} name- The name of the fragment to get, with its type; for instance, "blog-post.photo" * @returns {ImageView} view - The View object to manipulate */ getImageView: function getImageView(name, view) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.Image) { return fragment.getView(view); } if (fragment instanceof Fragments.StructuredText) { for (var i = 0; i < fragment.blocks.length; i++) { if (fragment.blocks[i].type == 'image') { return fragment.blocks[i]; } } } return null; }, // Useful for obsolete multiples getAllImageViews: function getAllImageViews(name, view) { return this.getAllImages(name).map(function (image) { return image.getView(view); }); }, /** * Gets the timestamp fragment in the current Document object, for further manipulation. * * @example document.getDate('blog-post.publicationdate').asHtml(linkResolver) * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.publicationdate" * @returns {Date} - The Date object to manipulate */ getTimestamp: function getTimestamp(name) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.Timestamp) { return fragment.value; } return null; }, /** * Gets the date fragment in the current Document object, for further manipulation. * * @example document.getDate('blog-post.publicationdate').asHtml(linkResolver) * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.publicationdate" * @returns {Date} - The Date object to manipulate */ getDate: function getDate(name) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.Date) { return fragment.value; } return null; }, /** * Gets a boolean value of the fragment in the current Document object, for further manipulation. * This works great with a Select fragment. The Select values that are considered true are (lowercased before matching): 'yes', 'on', and 'true'. * * @example if(document.getBoolean('blog-post.enableComments')) { ... } * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.enableComments" * @returns {boolean} - The boolean value of the fragment */ getBoolean: function getBoolean(name) { var fragment = this.get(name); return fragment.value && (fragment.value.toLowerCase() == 'yes' || fragment.value.toLowerCase() == 'on' || fragment.value.toLowerCase() == 'true'); }, /** * Gets the text fragment in the current Document object, for further manipulation. * The method works with StructuredText fragments, Text fragments, Number fragments, Select fragments and Color fragments. * * @example document.getText('blog-post.label').asHtml(linkResolver). * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.label" * @param {string} after - a suffix that will be appended to the value * @returns {object} - either StructuredText, or Text, or Number, or Select, or Color. */ getText: function getText(name, after) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.StructuredText) { return fragment.blocks.map(function (block) { if (block.text) { return block.text + (after ? after : ''); } return ''; }).join('\n'); } if (fragment instanceof Fragments.Text) { if (fragment.value) { return fragment.value + (after ? after : ''); } } if (fragment instanceof Fragments.Number) { if (fragment.value) { return fragment.value + (after ? after : ''); } } if (fragment instanceof Fragments.Select) { if (fragment.value) { return fragment.value + (after ? after : ''); } } if (fragment instanceof Fragments.Color) { if (fragment.value) { return fragment.value + (after ? after : ''); } } return null; }, /** * Gets the StructuredText fragment in the current Document object, for further manipulation. * @example document.getStructuredText('blog-post.body').asHtml(linkResolver) * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.body" * @returns {StructuredText} - The StructuredText fragment to manipulate. */ getStructuredText: function getStructuredText(name) { var fragment = this.get(name); if (fragment instanceof require('./fragments').StructuredText) { return fragment; } return null; }, /** * Gets the Link fragment in the current Document object, for further manipulation. * @example document.getLink('blog-post.link').url(resolver) * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.link" * @returns {WebLink|DocumentLink|ImageLink} - The Link fragment to manipulate. */ getLink: function getLink(name) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.WebLink || fragment instanceof Fragments.DocumentLink || fragment instanceof Fragments.FileLink || fragment instanceof Fragments.ImageLink) { return fragment; } return null; }, /** * Gets the Number fragment in the current Document object, for further manipulation. * @example document.getNumber('product.price') * * @param {string} name - The name of the fragment to get, with its type; for instance, "product.price" * @returns {number} - The number value of the fragment. */ getNumber: function getNumber(name) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.Number) { return fragment.value; } return null; }, /** * Gets the Color fragment in the current Document object, for further manipulation. * @example document.getColor('product.color') * * @param {string} name - The name of the fragment to get, with its type; for instance, "product.color" * @returns {string} - The string value of the Color fragment. */ getColor: function getColor(name) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.Color) { return fragment.value; } return null; }, /** Gets the GeoPoint fragment in the current Document object, for further manipulation. * * @example document.getGeoPoint('blog-post.location').asHtml(linkResolver) * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.location" * @returns {GeoPoint} - The GeoPoint object to manipulate */ getGeoPoint: function getGeoPoint(name) { var Fragments = require('./fragments'); var fragment = this.get(name); if (fragment instanceof Fragments.GeoPoint) { return fragment; } return null; }, /** * Gets the Group fragment in the current Document object, for further manipulation. * * @example document.getGroup('product.gallery').asHtml(linkResolver). * * @param {string} name - The name of the fragment to get, with its type; for instance, "product.gallery" * @returns {Group} - The Group fragment to manipulate. */ getGroup: function getGroup(name) { var fragment = this.get(name); if (fragment instanceof require('./fragments').Group) { return fragment; } return null; }, /** * Shortcut to get the HTML output of the fragment in the current document. * This is the same as writing document.get(fragment).asHtml(linkResolver); * * @param {string} name - The name of the fragment to get, with its type; for instance, "blog-post.body" * @param {function} linkResolver * @returns {string} - The HTML output */ getHtml: function getHtml(name, linkResolver) { if (!isFunction(linkResolver)) { // Backward compatibility with the old ctx argument var ctx = linkResolver; linkResolver = function linkResolver(doc, isBroken) { return ctx.linkResolver(ctx, doc, isBroken); }; } var fragment = this.get(name); if (fragment && fragment.asHtml) { return fragment.asHtml(linkResolver); } return null; }, /** * Transforms the whole document as an HTML output. Each fragment is separated by a &lt;section&gt; tag, * with the attribute data-field="nameoffragment" * Note that most of the time you will not use this method, but read fragment independently and generate * HTML output for {@link StructuredText} fragment with that class' asHtml method. * * @param {function} linkResolver * @returns {string} - The HTML output */ asHtml: function asHtml(linkResolver) { if (!isFunction(linkResolver)) { // Backward compatibility with the old ctx argument var ctx = linkResolver; linkResolver = function linkResolver(doc, isBroken) { return ctx.linkResolver(ctx, doc, isBroken); }; } var htmls = []; for (var field in this.fragments) { var fragment = this.get(field); htmls.push(fragment && fragment.asHtml ? '<section data-field="' + field + '">' + fragment.asHtml(linkResolver) + '</section>' : ''); } return htmls.join(''); }, /** * Turns the document into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function asText(linkResolver) { if (!isFunction(linkResolver)) { // Backward compatibility with the old ctx argument var ctx = linkResolver; linkResolver = function linkResolver(doc, isBroken) { return ctx.linkResolver(ctx, doc, isBroken); }; } var texts = []; for (var field in this.fragments) { var fragment = this.get(field); texts.push(fragment && fragment.asText ? fragment.asText(linkResolver) : ''); } return texts.join(''); }, /** * Linked documents, as an array of {@link DocumentLink} * @returns {Array} */ linkedDocuments: function linkedDocuments() { var i, j, link; var result = []; var Fragments = require('./fragments'); for (var field in this.data) { var fragment = this.get(field); if (fragment instanceof Fragments.DocumentLink) { result.push(fragment); } if (fragment instanceof Fragments.StructuredText) { for (i = 0; i < fragment.blocks.length; i++) { var block = fragment.blocks[i]; if (block.type == "image" && block.linkTo) { link = Fragments.initField(block.linkTo); if (link instanceof Fragments.DocumentLink) { result.push(link); } } var spans = block.spans || []; for (j = 0; j < spans.length; j++) { var span = spans[j]; if (span.type == "hyperlink") { link = Fragments.initField(span.data); if (link instanceof Fragments.DocumentLink) { result.push(link); } } } } } if (fragment instanceof Fragments.Group) { for (i = 0; i < fragment.value.length; i++) { result = result.concat(fragment.value[i].linkedDocuments()); } } if (fragment instanceof Fragments.SliceZone) { for (i = 0; i < fragment.value.length; i++) { var slice = fragment.value[i]; if (slice.value instanceof Fragments.DocumentLink) { result.push(slice.value); } } } } return result; }, /** * An array of the fragments with the given fragment name. * The array is often a single-element array, expect when the fragment is a multiple fragment. * @private */ _getFragments: function _getFragments(name) { if (!this.fragments || !this.fragments[name]) { return []; } if (Array.isArray(this.fragments[name])) { return this.fragments[name]; } else { return [this.fragments[name]]; } } }; /** * Embodies a document as returned by the API. * Most useful fields: id, type, tags, slug, slugs * @constructor * @global * @alias Doc */ function Document(id, uid, type, href, tags, slugs, firstPublicationDate, lastPublicationDate, lang, alternateLanguages, data, rawJSON) { /** * The ID of the document * @type {string} */ this.id = id; /** * The User ID of the document, a human readable id * @type {string|null} */ this.uid = uid; /** * The type of the document, corresponds to a document mask defined in the repository * @type {string} */ this.type = type; /** * The URL of the document in the API * @type {string} */ this.href = href; /** * The tags of the document * @type {array} */ this.tags = tags; /** * The current slug of the document, "-" if none was provided * @type {string} */ this.slug = slugs ? slugs[0] : "-"; /** * All the slugs that were ever used by this document (including the current one, at the head) * @type {array} */ this.slugs = slugs; /** * same as fragments */ this.data = data; /** * raw JSON from the API */ this.rawJSON = rawJSON; /** * The first publication date of the document */ this.firstPublicationDate = DateUtils.parse(firstPublicationDate); /** * The last publication date of the document */ this.lastPublicationDate = DateUtils.parse(lastPublicationDate); /** * The language code of the document */ this.lang = lang ? lang : null; /** * The alternate language versions of the document */ this.alternateLanguages = alternateLanguages ? alternateLanguages : []; /** * Fragments, converted to business objects */ this.fragments = require('./fragments').parseFragments(data); } Document.prototype = Object.create(WithFragments.prototype); /** * Gets the SliceZone fragment in the current Document object, for further manipulation. * * @example document.getSliceZone('product.gallery').asHtml(linkResolver). * * @param {string} name - The name of the fragment to get, with its type; for instance, "product.gallery" * @returns {Group} - The SliceZone fragment to manipulate. */ Document.prototype.getSliceZone = function (name) { var fragment = this.get(name); if (fragment instanceof require('./fragments').SliceZone) { return fragment; } return null; }; function GroupDoc(data) { /** * The original JSON data from the API */ this.data = data; /** * Fragments, converted to business objects */ this.fragments = require('./fragments').parseFragments(data); } GroupDoc.prototype = Object.create(WithFragments.prototype); // -- Private helpers function isFunction(f) { var getType = {}; return f && getType.toString.call(f) === '[object Function]'; } module.exports = { WithFragments: WithFragments, Document: Document, GroupDoc: GroupDoc }; },{"./fragments":7,"./utils/date":13}],6:[function(require,module,exports){ "use strict"; /** * A collection of experiments currently available * @param data the json data received from the Prismic API * @constructor */ function Experiments(data) { var drafts = []; var running = []; if (data) { data.drafts && data.drafts.forEach(function (exp) { drafts.push(new Experiment(exp)); }); data.running && data.running.forEach(function (exp) { running.push(new Experiment(exp)); }); } this.drafts = drafts; this.running = running; } Experiments.prototype.current = function () { return this.running.length > 0 ? this.running[0] : null; }; /** * Get the current running experiment variation ref from a cookie content */ Experiments.prototype.refFromCookie = function (cookie) { if (!cookie || cookie.trim() === "") return null; var splitted = cookie.trim().split(" "); if (splitted.length < 2) return null; var expId = splitted[0]; var varIndex = parseInt(splitted[1], 10); var exp = this.running.filter(function (exp) { return exp.googleId() == expId && exp.variations.length > varIndex; })[0]; return exp ? exp.variations[varIndex].ref() : null; }; function Experiment(data) { this.data = data; var variations = []; data.variations && data.variations.forEach(function (v) { variations.push(new Variation(v)); }); this.variations = variations; } Experiment.prototype.id = function () { return this.data.id; }; Experiment.prototype.googleId = function () { return this.data.googleId; }; Experiment.prototype.name = function () { return this.data.name; }; functi