UNPKG

prismic.io

Version:

JavaScript development kit for prismic.io

1,417 lines (1,295 loc) 36.1 kB
"use strict"; var documents = require('./documents'); var DateUtils = require('./utils/date'); var WithFragments = documents.WithFragments; var GroupDoc = documents.GroupDoc; /** * Embodies a plain text fragment (beware: not a structured text) * @constructor * @global * @alias Fragments:Text */ function Text(data) { this.value = data; } Text.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<span>" + this.value + "</span>"; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return this.value; } }; /** * Embodies a document link fragment (a link that is internal to a prismic.io repository) * @constructor * @global * @alias Fragments:DocumentLink */ function DocumentLink(data) { this.value = data; this.document = data.document; /** * @field * @description the linked document id */ this.id = data.document.id; /** * @field * @description the linked document uid */ this.uid = data.document.uid; /** * @field * @description the linked document tags */ this.tags = data.document.tags; /** * @field * @description the linked document slug */ this.slug = data.document.slug; /** * @field * @description the linked document type */ this.type = data.document.type; /** * @field * @description the linked document language */ this.lang = data.document.lang; var fragmentsData = {}; if (data.document.data) { for (var field in data.document.data[data.document.type]) { fragmentsData[data.document.type + '.' + field] = data.document.data[data.document.type][field]; } } /** * @field * @description the fragment list, if the fetchLinks parameter was used in at query time */ this.fragments = parseFragments(fragmentsData); /** * @field * @description true if the link is broken, false otherwise */ this.isBroken = data.isBroken; } DocumentLink.prototype = Object.create(WithFragments.prototype); /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @params {object} ctx - mandatory ctx object, with a useable linkResolver function (please read prismic.io online documentation about this) * @returns {string} - basic HTML code for the fragment */ DocumentLink.prototype.asHtml = function (ctx) { return "<a href=\""+this.url(ctx)+"\">"+this.url(ctx)+"</a>"; }; /** * Returns the URL of the document link. * * @params {object} linkResolver - mandatory linkResolver function (please read prismic.io online documentation about this) * @returns {string} - the proper URL to use */ DocumentLink.prototype.url = function (linkResolver) { return linkResolver(this, this.isBroken); }; /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ DocumentLink.prototype.asText = function(linkResolver) { return this.url(linkResolver); }; /** * Embodies a web link fragment * @constructor * @global * @alias Fragments:WebLink */ function WebLink(data) { /** * @field * @description the JSON object exactly as is returned in the "data" field of the JSON responses (see API documentation: https://developers.prismic.io/documentation/UjBe8bGIJ3EKtgBZ/api-documentation#json-responses) */ this.value = data; } WebLink.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<a href=\""+this.url()+"\">"+this.url()+"</a>"; }, /** * Returns the URL of the link. * * @returns {string} - the proper URL to use */ url: function() { return this.value.url; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return this.url(); } }; /** * Embodies a file link fragment * @constructor * @global * @alias Fragments:FileLink */ function FileLink(data) { /** * @field * @description the JSON object exactly as is returned in the "data" field of the JSON responses (see API documentation: https://developers.prismic.io/documentation/UjBe8bGIJ3EKtgBZ/api-documentation#json-responses) */ this.value = data; } FileLink.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<a href=\""+this.url()+"\">"+this.value.file.name+"</a>"; }, /** * Returns the URL of the link. * * @returns {string} - the proper URL to use */ url: function() { return this.value.file.url; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return this.url(); } }; /** * Embodies an image link fragment * @constructor * @global * @alias Fragments:ImageLink */ function ImageLink(data) { /** * * @field * @description the JSON object exactly as is returned in the "data" field of the JSON responses (see API documentation: https://developers.prismic.io/documentation/UjBe8bGIJ3EKtgBZ/api-documentation#json-responses) */ this.value = data; } ImageLink.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<a href=\"" + this.url() + "\"><img src=\"" + this.url() + "\" alt=\"" + (this.alt || "") + " copyright=\"" + (this.copyright || "") + "\" \"></a>"; }, /** * Returns the URL of the link. * * @returns {string} - the proper URL to use */ url: function() { return this.value.image.url; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return this.url(); } }; /** * Embodies a select fragment * @constructor * @global * @alias Fragments:Select */ function Select(data) { /** * @field * @description the text value of the fragment */ this.value = data; } Select.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<span>" + this.value + "</span>"; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return this.value; } }; /** * Embodies a color fragment * @constructor * @global * @alias Fragments:Color */ function Color(data) { /** * @field * @description the text value of the fragment */ this.value = data; } Color.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<span>" + this.value + "</span>"; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return this.value; } }; /** * Embodies a geopoint * @constructor * @global * @alias Fragments:GeoPoint */ function GeoPoint(data) { /** * @field * @description the latitude of the geo point */ this.latitude = data.latitude; /** * @field * @description the longitude of the geo point */ this.longitude = data.longitude; } GeoPoint.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return '<div class="geopoint"><span class="latitude">' + this.latitude + '</span><span class="longitude">' + this.longitude + '</span></div>'; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return '(' + this.latitude + "," + this.longitude + ')'; } }; /** * Embodies a Number fragment * @constructor * @global * @alias Fragments:Num */ function Num(data) { /** * @field * @description the integer value of the fragment */ this.value = data; } Num.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<span>" + this.value + "</span>"; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { if (this.value === null) { return null; } else { return this.value.toString(); } } }; /** * Embodies a Date fragment * @constructor * @global * @alias Fragments:Date */ function DateFragment(data) { /** * @field * @description the Date value of the fragment (as a regular JS Date object) */ this.value = new Date(data); } DateFragment.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<time>" + this.value + "</time>"; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { if (this.value === null) { return null; } else { return this.value.toString(); } } }; /** * Embodies a Timestamp fragment * @constructor * @global * @alias Fragments:Timestamp */ function Timestamp(data) { /** * @field * @description the Date value of the fragment (as a regular JS Date object) */ this.value = DateUtils.parse(data); } Timestamp.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return "<time>" + this.value + "</time>"; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { if (this.value === null) { return null; } else { return this.value.toString(); } } }; /** * Embodies an embed fragment * @constructor * @global * @alias Fragments:Embed */ function Embed(data) { /** * @field * @description the JSON object exactly as is returned in the "data" field of the JSON responses (see API documentation: https://developers.prismic.io/documentation/UjBe8bGIJ3EKtgBZ/api-documentation#json-responses) */ this.value = data; } Embed.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return this.value.oembed.html; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return ""; } }; /** * Embodies an Image fragment * @constructor * @global * @alias Fragments:ImageEl */ function ImageEl(main, views) { /** * @field * @description the main ImageView for this image */ this.main = main; /** * @field * @description the url of the main ImageView for this image */ this.url = main.url; /** * @field * @description an array of all the other ImageViews for this image */ this.views = views || {}; } ImageEl.prototype = { /** * Gets the view of the image, from its name * * @param {string} name - the name of the view to get * @returns {ImageView} - the proper view */ getView: function(name) { if (name === "main") { return this.main; } else { return this.views[name]; } }, /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return this.main.asHtml(); }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return ""; } }; /** * Embodies an image view (an image in prismic.io can be defined with several different thumbnail sizes, each size is called a "view") * @constructor * @global * @alias Fragments:ImageView */ function ImageView(url, width, height, alt, copyright) { /** * @field * @description the URL of the ImageView (useable as it, in a <img> tag in HTML, for instance) */ this.url = url; /** * @field * @description the width of the ImageView */ this.width = width; /** * @field * @description the height of the ImageView */ this.height = height; /** * @field * @description the alt text for the ImageView */ this.alt = alt; /** * @field * @description the copyright for the ImageView */ this.copyright = copyright; } ImageView.prototype = { ratio: function () { return this.width / this.height; }, /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function () { return '<img src="' + this.url + '" width="' + this.width + '" height="' + this.height + '" alt="' + (this.alt || "") + '" copyright="' + (this.copyright || "") + '">'; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { return ""; } }; /** * Embodies a fragment of type "Separator" (only used in Slices) * @constructor * @global * @alias Fragments:Separator */ function Separator() { } Separator.prototype = { asHtml: function() { return "<hr/>"; }, asText: function() { return "----"; } }; /** * Embodies a fragment of type "Group" (which is a group of subfragments) * @constructor * @global * @alias Fragments:Group */ function Group(data) { this.value = []; for (var i = 0; i < data.length; i++) { this.value.push(new GroupDoc(data[i])); } } Group.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * @params {function} linkResolver - linkResolver function (please read prismic.io online documentation about this) * @returns {string} - basic HTML code for the fragment */ asHtml: function(linkResolver) { var output = ""; for (var i = 0; i < this.value.length; i++) { output += this.value[i].asHtml(linkResolver); } return output; }, /** * Turns the Group fragment into an array in order to access its items (groups of fragments), * or to loop through them. * @params {object} ctx - mandatory ctx object, with a useable linkResolver function (please read prismic.io online documentation about this) * @returns {Array} - the array of groups, each group being a JSON object with subfragment name as keys, and subfragment as values */ toArray: function(){ return this.value; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function(linkResolver) { var output = ""; for (var i=0; i<this.value.length; i++) { output += this.value[i].asText(linkResolver) + '\n'; } return output; }, getFirstImage: function() { return this.toArray().reduce(function(image, fragment) { if (image) return image; else { return fragment.getFirstImage(); } }, null); }, getFirstTitle: function() { return this.toArray().reduce(function(st, fragment) { if (st) return st; else { return fragment.getFirstTitle(); } }, null); }, getFirstParagraph: function() { return this.toArray().reduce(function(st, fragment) { if (st) return st; else { return fragment.getFirstParagraph(); } }, null); } }; /** * Embodies a structured text fragment * @constructor * @global * @alias Fragments:StructuredText */ function StructuredText(blocks) { this.blocks = blocks; } StructuredText.prototype = { /** * @returns {object} the first heading block in the text */ getTitle: function () { for(var i=0; i<this.blocks.length; i++) { var block = this.blocks[i]; if(block.type.indexOf('heading') === 0) { return block; } } return null; }, /** * @returns {object} the first block of type paragraph */ getFirstParagraph: function() { for(var i=0; i<this.blocks.length; i++) { var block = this.blocks[i]; if(block.type == 'paragraph') { return block; } } return null; }, /** * @returns {array} all paragraphs */ getParagraphs: function() { var paragraphs = []; for(var i=0; i<this.blocks.length; i++) { var block = this.blocks[i]; if(block.type == 'paragraph') { paragraphs.push(block); } } return paragraphs; }, /** * @returns {object} the nth paragraph */ getParagraph: function(n) { return this.getParagraphs()[n]; }, /** * @returns {object} */ getFirstImage: function() { for(var i=0; i<this.blocks.length; i++) { var block = this.blocks[i]; if(block.type == 'image') { return new ImageView( block.url, block.dimensions.width, block.dimensions.height, block.alt, block.copyright ); } } return null; }, /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * @params {function} linkResolver - please read prismic.io online documentation about link resolvers * @params {function} htmlSerializer optional HTML serializer to customize the output * @returns {string} - basic HTML code for the fragment */ asHtml: function(linkResolver, htmlSerializer) { var blockGroups = [], blockGroup, block, html = []; if (!isFunction(linkResolver)) { // Backward compatibility with the old ctx argument var ctx = linkResolver; linkResolver = function(doc, isBroken) { return ctx.linkResolver(ctx, doc, isBroken); }; } if (Array.isArray(this.blocks)) { for(var i=0; i < this.blocks.length; i++) { block = this.blocks[i]; // Resolve image links if (block.type == "image" && block.linkTo) { var link = initField(block.linkTo); block.linkUrl = link.url(linkResolver); } if (block.type !== "list-item" && block.type !== "o-list-item") { // it's not a type that groups blockGroups.push(block); blockGroup = null; } else if (!blockGroup || blockGroup.type != ("group-" + block.type)) { // it's a new type or no BlockGroup was set so far blockGroup = { type: "group-" + block.type, blocks: [block] }; blockGroups.push(blockGroup); } else { // it's the same type as before, no touching blockGroup blockGroup.blocks.push(block); } } var blockContent = function(block) { var content = ""; if (block.blocks) { block.blocks.forEach(function (block2) { content = content + serialize(block2, blockContent(block2), htmlSerializer); }); } else { content = insertSpans(block.text, block.spans, linkResolver, htmlSerializer); } return content; }; blockGroups.forEach(function (blockGroup) { html.push(serialize(blockGroup, blockContent(blockGroup), htmlSerializer)); }); } return html.join(''); }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function() { var output = []; for(var i=0; i<this.blocks.length; i++) { var block = this.blocks[i]; if (block.text) { output.push(block.text); } } return output.join(' '); } }; function htmlEscape(input) { return input && input.replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/\n/g, "<br>"); } /** * Parses a block that has spans, and inserts the proper HTML code. * * @param {string} text - the original text of the block * @param {object} spans - the spans as returned by the API * @param {object} linkResolver - the function to build links that may be in the fragment (please read prismic.io's online documentation about this) * @param {function} htmlSerializer - optional serializer * @returns {string} - the HTML output */ function insertSpans(text, spans, linkResolver, htmlSerializer) { if (!spans || !spans.length) { return htmlEscape(text); } var tagsStart = {}; var tagsEnd = {}; spans.forEach(function (span) { if (!tagsStart[span.start]) { tagsStart[span.start] = []; } if (!tagsEnd[span.end]) { tagsEnd[span.end] = []; } tagsStart[span.start].push(span); tagsEnd[span.end].unshift(span); }); var c; var html = ""; var stack = []; for (var pos = 0, len = text.length + 1; pos < len; pos++) { // Looping to length + 1 to catch closing tags if (tagsEnd[pos]) { tagsEnd[pos].forEach(function () { // Close a tag var tag = stack.pop(); // Continue only if block contains content. if (typeof tag !== 'undefined') { var innerHtml = serialize(tag.span, tag.text, htmlSerializer); if (stack.length === 0) { // The tag was top level html += innerHtml; } else { // Add the content to the parent tag stack[stack.length - 1].text += innerHtml; } } }); } if (tagsStart[pos]) { // Sort bigger tags first to ensure the right tag hierarchy tagsStart[pos].sort(function (a, b) { return (b.end - b.start) - (a.end - a.start); }); tagsStart[pos].forEach(function (span) { // Open a tag var url = null; if (span.type == "hyperlink") { var fragment = initField(span.data); if (fragment) { url = fragment.url(linkResolver); } else { if (console && console.error) console.error('Impossible to convert span.data as a Fragment', span); return; } span.url = url; } var elt = { span: span, text: "" }; stack.push(elt); }); } if (pos < text.length) { c = text[pos]; if (stack.length === 0) { // Top-level text html += htmlEscape(c); } else { // Inner text of a span stack[stack.length - 1].text += htmlEscape(c); } } } return html; } /** * Embodies a simple slice (fragment or group) * @constructor * @global * @alias Fragments:SimpleSlice */ function SimpleSlice(sliceType, label, sliceValue) { this.sliceType = sliceType; this.label = label; this.value = initField(sliceValue); } SimpleSlice.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function (linkResolver) { var classes = ['slice']; if (this.label) classes.push(this.label); return '<div data-slicetype="' + this.sliceType + '" class="' + classes.join(' ') + '">' + this.value.asHtml(linkResolver) + '</div>'; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function(linkResolver) { return this.value.asText(linkResolver); }, /** * Get the first Image in slice. * @returns {object} */ getFirstImage: function() { var fragment = this.value; if(typeof fragment.getFirstImage === "function") { return fragment.getFirstImage(); } else if (fragment instanceof ImageEl) { return fragment; } else return null; }, getFirstTitle: function() { var fragment = this.value; if(typeof fragment.getFirstTitle === "function") { return fragment.getFirstTitle(); } else if (fragment instanceof StructuredText) { return fragment.getTitle(); } else return null; }, getFirstParagraph: function() { var fragment = this.value; if(typeof fragment.getFirstParagraph === "function") { return fragment.getFirstParagraph(); } else return null; } }; /** * Embodies a composite slice with repeatable and non repeatable parts * @constructor * @global * @alias Fragments:CompositeSlice */ function CompositeSlice(sliceType, label, sliceValue) { this.sliceType = sliceType; this.label = label; var nonRepeatKeys = Object.keys(sliceValue['non-repeat']); this.nonRepeat = nonRepeatKeys.reduce(function(acc, key) { var field = initField(sliceValue['non-repeat'][key]); acc[key] = field; return acc; }, {}); this.repeat = initField({type: "Group", value: sliceValue.repeat}); } CompositeSlice.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function(linkResolver) { var classes = ['slice']; if (this.label) classes.push(this.label); var self = this; var nonRepeatHtml = Object.keys(this.nonRepeat).reduce(function(acc, key) { return acc + self.nonRepeat[key].asHtml(linkResolver); }, ""); return '<div data-slicetype="' + this.sliceType + '" class="' + classes.join(' ') + '">' + nonRepeatHtml + this.repeat.asHtml(linkResolver) + '</div>'; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function(linkResolver) { var self = this; var nonRepeatText = Object.keys(this.nonRepeat).reduce(function(acc, key) { return acc + self.nonRepeat[key].asText(linkResolver); }, ""); return nonRepeatText + '\n' + this.repeat.asText(linkResolver); }, /** * Get the first Image in slice. * @returns {object} */ getFirstImage: function() { var self = this; var firstImage = Object.keys(this.nonRepeat).reduce(function(image, key) { if (image) { return image; } else { var element = self.nonRepeat[key]; if(typeof element.getFirstImage === "function") { return element.getFirstImage(); } else if (element instanceof ImageEl) { return element; } else return null; } }, null); if (firstImage) { return firstImage; } else { return this.repeat.getFirstImage(); } }, getFirstTitle: function() { var self = this; var firstTitle = Object.keys(this.nonRepeat).reduce(function(title, key) { if (title) return title; else { var fragment = self.nonRepeat[key]; if(typeof fragment.getFirstTitle === "function") { return fragment.getFirstTitle(); } else if (fragment instanceof StructuredText) { return fragment.getTitle(); } else return null; } }, null); return firstTitle || this.repeat.getFirstTitle(); }, getFirstParagraph: function() { var self = this; var firstParagraph = Object.keys(this.nonRepeat).reduce(function(paragraph, key) { if (paragraph) return paragraph; else { var fragment = self.nonRepeat[key]; if(typeof fragment.getFirstParagraph === "function") { return fragment.getFirstParagraph(); } else return null; } }, null); return firstParagraph || this.repeat.getFirstParagraph(); } }; /** * Embodies a SliceZone fragment * @constructor * @global * @alias Fragments:SliceZone */ function SliceZone(data) { this.value = []; for (var i = 0; i < data.length; i++) { var sliceType = data[i]['slice_type']; var label = data[i]['slice_label'] || null; var value = data[i]; if (sliceType && value) { if(value.repeat) this.value.push(new CompositeSlice(sliceType, label, value)); else this.value.push(new SimpleSlice(sliceType, label, value.value)); } } this.slices = this.value; } SliceZone.prototype = { /** * Turns the fragment into a useable HTML version of it. * If the native HTML code doesn't suit your design, this function is meant to be overriden. * * @returns {string} - basic HTML code for the fragment */ asHtml: function (linkResolver) { var output = ""; for (var i = 0; i < this.value.length; i++) { output += this.value[i].asHtml(linkResolver); } return output; }, /** * Turns the fragment into a useable text version of it. * * @returns {string} - basic text version of the fragment */ asText: function(linkResolver) { var output = ""; for (var i = 0; i < this.value.length; i++) { output += this.value[i].asText(linkResolver) + '\n'; } return output; }, getFirstImage: function() { return this.value.reduce(function(image, slice) { if (image) return image; else { return slice.getFirstImage(); } }, null); }, getFirstTitle: function() { return this.value.reduce(function(text, slice) { if (text) return text; else { return slice.getFirstTitle(); } }, null); }, getFirstParagraph: function() { return this.value.reduce(function(paragraph, slice) { if (paragraph) return paragraph; else { return slice.getFirstParagraph(); } }, null); } }; /** * From a fragment's name, casts it into the proper object type (like Prismic.Fragments.StructuredText) * * @private * @param {string} field - the fragment's name * @returns {object} - the object of the proper Fragments type. */ function initField(field) { var classForType = { "Color": Color, "Number": Num, "Date": DateFragment, "Timestamp": Timestamp, "Text": Text, "Embed": Embed, "GeoPoint": GeoPoint, "Select": Select, "StructuredText": StructuredText, "Link.document": DocumentLink, "Link.web": WebLink, "Link.file": FileLink, "Link.image": ImageLink, "Separator": Separator, "Group": Group, "SliceZone": SliceZone }; if (classForType[field.type]) { return new classForType[field.type](field.value); } if (field.type === "Image") { var img = field.value.main; var output = new ImageEl( new ImageView( img.url, img.dimensions.width, img.dimensions.height, img.alt, img.copyright ), {} ); for (var name in field.value.views) { img = field.value.views[name]; output.views[name] = new ImageView( img.url, img.dimensions.width, img.dimensions.height, img.alt, img.copyright ); } return output; } if (console && console.log) console.log("Fragment type not supported: ", field.type); return null; } function parseFragments(json) { var result = {}; for (var key in json) { if (json.hasOwnProperty(key)) { if (Array.isArray(json[key])) { result[key] = json[key].map(function (fragment) { return initField(fragment); }); } else { result[key] = initField(json[key]); } } } return result; } function isFunction(f) { var getType = {}; return f && getType.toString.call(f) === '[object Function]'; } function serialize(element, content, htmlSerializer) { // Return the user customized output (if available) if (htmlSerializer) { var custom = htmlSerializer(element, content); if (custom) { return custom; } } // Fall back to the default HTML output var TAG_NAMES = { "heading1": "h1", "heading2": "h2", "heading3": "h3", "heading4": "h4", "heading5": "h5", "heading6": "h6", "paragraph": "p", "preformatted": "pre", "list-item": "li", "o-list-item": "li", "group-list-item": "ul", "group-o-list-item": "ol", "strong": "strong", "em": "em" }; if (TAG_NAMES[element.type]) { var name = TAG_NAMES[element.type]; var classCode = element.label ? (' class="' + element.label + '"') : ''; return '<' + name + classCode + '>' + content + '</' + name + '>'; } if (element.type == "image") { var label = element.label ? (" " + element.label) : ""; var imgTag = '<img src="' + element.url + '" alt="' + (element.alt || "") + '" copyright="' + (element.copyright || "") + '">'; return '<p class="block-img' + label + '">' + (element.linkUrl ? ('<a href="' + element.linkUrl + '">' + imgTag + '</a>') : imgTag) + '</p>'; } if (element.type == "embed") { return '<div data-oembed="'+ element.embed_url + '" data-oembed-type="'+ element.type + '" data-oembed-provider="'+ element.provider_name + (element.label ? ('" class="' + element.label) : '') + '">' + element.oembed.html+"</div>"; } if (element.type === 'hyperlink') { return '<a href="' + element.url + '">' + content + '</a>'; } if (element.type === 'label') { return '<span class="' + element.data.label + '">' + content + '</span>'; } return "<!-- Warning: " + element.type + " not implemented. Upgrade the Developer Kit. -->" + content; } module.exports = { Embed: Embed, Image: ImageEl, ImageView: ImageView, Text: Text, Number: Num, Date: DateFragment, Timestamp: Timestamp, Select: Select, Color: Color, StructuredText: StructuredText, WebLink: WebLink, DocumentLink: DocumentLink, ImageLink: ImageLink, FileLink: FileLink, Separator: Separator, Group: Group, GeoPoint: GeoPoint, SliceZone: SliceZone, SimpleSlice: SimpleSlice, CompositeSlice: CompositeSlice, initField: initField, parseFragments: parseFragments, insertSpans: insertSpans };