﻿//-----------------------------------------------------------------------------
// Atom
//
// Copyright 2005-2010 - Xcential Group LLC.
//
//-----------------------------------------------------------------------------

Atom.prototype = new XObject;
Atom.prototype.constructor = Atom;

//=============================================================================
// Constructor

function Atom(
   feed,
   create
)
{
   feed = (feed == null) ? Atom.createFeed() : feed;
   create = (create == null) ? true : false;

   if (create)
      return new Atom(feed, false);

   //--------------------------------------------------------------------------
   // Private Interface

   //--------------------------------------------------------------------------
   // Privileged Interface


   this.valueOf = function()
   {

      return oFeed;
   }

   //--------------------------------------------------------------------------

   this.setObjectValue = function(
      feed
   )
   {
      feed = (feed == null) ? null : feed;

      oFeed = feed;

      return oFeed;
   }

   //--------------------------------------------------------------------------
   // Initialization

   var oFeed = this.setObjectValue(feed);

   this.linkCustomArgs = "";

}

Atom.ClassName = "Atom";

//=============================================================================
// Static Interface

Atom.NAMESPACE_URI = "http://www.w3.org/2005/Atom";
Atom.PREFERRED_PREFIX = "atom";

Atom.MEDIA_TYPE = "application/atom+xml";
Atom.FORMAT = "atom";

Atom.HIDE = true;
Atom.SHOW = false;

Atom.VERIFY = true;

//-----------------------------------------------------------------------------

Atom.createFeed = function(
   customNamespaces
)
{
   customNamespaces = (customNamespaces == null) ? null : customNamespaces;

   var feed = {
      id:           XUtils.generateId(),
      title:        null,
      subtitle:     null,
      icon:         null,
      author:       null,
      generator:    XApp.APPLICATION_NAME,
      updated:      XDate().toString(XDate.STYLE_RFC_3339),
      totalResults: null,
      itemsPerPage: null,
      startIndex:   null,
      links:        [],
      categories:   [],
      customProps:  [],
      entries:      []
   }

   feed.customProps.namespaces = (customNamespaces) ? customNamespaces : null;

   return feed;
}

//-----------------------------------------------------------------------------

Atom.createEntry = function(
   id,
   type,
   alternateURL,
   browse
)
{
   id = (id == null) ? XUtils.generateId() : id;
   type = (type == null) ? null : type;
   alternateURL = (alternateURL == null) ? null : alternateURL;
   browse = (browse == null) ? null : browse;

   var entry = {
      id:           id,
      type:         type,
      imgLink:      null,
      title:        null,
      description:  null,
      summary:      null,
      summaryHTML:  null,
      summaryXHTML: null,
      content:      null,
      contentHTML:  null,
      contentXHTML: null,
      author:       null,
      updated:      null,
      attributes:   [],
      links:        [],
      categories:   [],
      customProps:  []
   }

   if (alternateURL || browse)
      entry.links.push(Atom.createLink("alternate", alternateURL, browse, "text/html", null));

   if (type)
      entry.categories.push(Atom.createCategory("type", type, null, Atom.HIDE));

   return entry;
}

//-----------------------------------------------------------------------------

Atom.createAttribute = function(
   name,
   value
)
{

   var attribute = {
      name:  name,
      value: value
   }

   return attribute;
}

//-----------------------------------------------------------------------------

Atom.createProp = function(
   name,
   value,
   type,
   label,
   attributes
)
{
   type = (type == null) ? null : type;
   label = (label == null) ? null : label;
   attributes = (attributes == null) ? [] : attributes;

   var prop = {
      name:       name,
      value:      value,
      type:       type,
      label:      label,
      attributes: attributes
   }

   return prop;
}

//-----------------------------------------------------------------------------

Atom.createCategory = function(
   scheme,
   term,
   label,
   hide
)
{
   scheme = (scheme == null) ? "unspecified" : scheme.toLowerCase();
   label = (label == null) ? null : label;
   hide = (hide == null) ? false : hide;

   if (!(/^urn:/).test(scheme) && !(/^http:/).test(scheme))
      scheme = "urn:legisweb-com:category:" + scheme;

   var category = new Array();
   category.term = term;
   category.scheme = scheme;
   category.label = label;
   category.hide = hide;
   category.sortOrder = 0;

   return category;
}

//--------------------------------------------------------------------------

Atom.createLink = function(
   rel,
   ref,
   browse,
   type,
   title
)
{
   ref = (ref == null) ? null : ref;
   browse = (browse == null) ? null : browse;
   type = (type == null) ? null : type;
   title = (title == null) ? null : title;

   var link = new Array();
   link.rel = rel;
   link.ref = ref;
   link.browse = browse;
   link.type = type;
   link.title = title;

   return link;
}

//-----------------------------------------------------------------------------

Atom.getCategory = function(
   entry,
   scheme
)
{
   scheme = (scheme == null) ? "category" : scheme;

   for (var i=0; i<entry.categories.length; i++)
   {
      var category = entry.categories[i];
      if (category.scheme == scheme || (RegExp("-" + scheme + "$")).test(category.scheme))
         return category.term;
   }

   return null;
}

//-----------------------------------------------------------------------------

Atom.decrypt = function(
   keyChain,
   textX,
   name
)
{
   keyChain = (keyChain == null) ? null : keyChain;
   textX = (textX == null) ? null : textX;
   name = (name == null) ? "text" : name;

   var text = null;
   if (textX && textX.length > 0 && keyChain)
   {
      try
      {
         var textValue = XCipher(keyChain).decrypt(textX);
         var textType = "text";
         if ((/^<\!\[CDATA\[/).test(textValue))
         {
            textType = "html";
            textValue = textValue.replace(/<\!\[CDATA\[/,"").replace(/\]\]>/,"");
         }
         else if ((/xhtml\:/).test(textValue) || (/http\:\/\/www.w3.org\/1999\/xhtml/).test(textValue))
         {
            textType = "xhtml";
            textValue = XHTML.fix(textValue);
         }
         text = {type: textType, value: textValue};
      }
      catch (error) { text = {type: "html", value: "<span class=\"Alert\"><b>Error:</b> The " + name + " cannot be decrypted.</span>"}; }
   }

   return text;
}

//-----------------------------------------------------------------------------

Atom.getValue = function(
   entryXNode,
   childName,
   defaultValue,
   verify
)
{
   defaultValue = (defaultValue == null) ? null : defaultValue;
   verify = (verify == null) ? false : verify;

   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

   function retargetLinks(
      text
   )
   {
      text = (text == null) ? null : text;

      if (!text)
         return text;

      var newText = "";
      while (XMatch(text, /(<a\s[^>]*)/))
      {
         newText += XMatch.leftContext;
         text = XMatch.rightContext;
         var link = XMatch.matches[1];
         link = link.replace(/\s+target=[^\s\>]+/,"");
         link += " target=\"_blank\"";
         newText += link;
      }
      newText += text;

      return newText;
   }

   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

   function verify(
      type,
      value
   )
   {

      switch (type)
      {
         case "text":
            value = value.replace(/<[^>]+>/g," ");
            break;
         case "html":
            value = value.replace(/\<\!\[CDATA\[/ig,"").replace(/\]\]\>/ig,"");
            break;
         case "xhtml":
            try
            {
               value = XDoc(value).toXML();
            }
            catch (error)
            {
               value = "";
            }
            break
      }

      return value;
   }

   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

   var childXNode = entryXNode.getChild(childName);
   if (childXNode)
   {
      var type = childXNode.getAttribute("type", "text");
      var value = childXNode.toXML(XNode.INNER_ONLY);
      if (verify)
         value = verify(type, value);
      return (XString(value).isSomething()) ? {type: type, value: retargetLinks(value)} : defaultValue;
   }

   return defaultValue;
}

//-----------------------------------------------------------------------------

Atom.wrapDiv = function(
   xhtml
)
{

   return "<xhtml:div>\n   " + xhtml + "\n</xhtml:div>\n";
}

//-----------------------------------------------------------------------------

Atom.composeText = function(
   field,
   tagName
)
{

   var fieldXML = "";
   if (field)
      fieldXML += "   <" + tagName + ">" + XString(field).encode(XString.ENCODE_ENTITY) + "</" + tagName + ">\n";

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.composeURL = function(
   field,
   tagName
)
{

   var fieldXML = "";
   if (field)
   {
      var fieldURL = ((/^http:/).test(field)) ? field : XApp.getDomainURL() + field;
      fieldXML += "   <" + tagName + ">" + XString(fieldURL).encode(XString.ENCODE_ENTITY) + "</" + tagName + ">\n";
   }

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.composeStructuredContent = function(
   field,
   tagName
)
{

   var fieldXML = "";
   if (field && ((typeof(field) == "string" && XString(field).isSomething()) || XString(field.value).isSomething()))
   {
      if (typeof(field) == "string")
      {
         fieldXML += "   <" + tagName + ">" + XString(field).encode(XString.ENCODE_ENTITY) + "</" + tagName + ">\n";
      }
      else
      {
         switch (field.type)
         {
            case "text":
               fieldXML += "   <" + tagName + " type=\"text\">\n";
               fieldXML += "      " + XString(field.value).encode(XString.ENCODE_ENTITY) + "\n";
               fieldXML += "   </" + tagName + ">\n";
               break;
            case "html":
               fieldXML += "   <" + tagName + " type=\"html\">\n";
               fieldXML += "      <![CDATA[" + field.value + "]]>\n";
               fieldXML += "   </" + tagName + ">\n";
               break;
            case "xhtml":
               fieldXML += "   <" + tagName + " type=\"xhtml\">\n";
               fieldXML += "      " + field.value + "\n";
               fieldXML += "   </" + tagName + ">\n";
               break;
         }
      }
   }

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.composePerson = function(
   field,
   tagName
)
{

   var fieldXML = "";
   if (field && field.length > 0)
      fieldXML += "   <" + tagName + "><name>" + XString(field).encode(XString.ENCODE_ENTITY) + "</name></" + tagName + ">\n";

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.composeDate = function(
   field,
   tagName
)
{

   var fieldXML = "";
   if (field)
      fieldXML += "   <" + tagName + ">" + XDate(field).toString(XDate.STYLE_RFC_3339) + "</" + tagName + ">\n";

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.composeCollection = function(
   collection,
   tagName
)
{
   tagName = (tagName == null) ? null : tagName;

   var fieldXML = "";
   if (collection)
   {
      for (var i=0; i<collection.length; i++)
      {
         var field = collection[i];
         fieldXML += "   <" + ((field.name) ? field.name : tagName) + " " +
            ((field.rel)       ? "rel=\"" + field.rel + "\" " : "") +
            ((field.ref)       ? "href=\"" + XString(XRef(field.ref).addArgs(this.linkCustomArgs)).encode(XString.ENCODE_ENTITY) + "\" " : "") +
            ((field.type)      ? "type=\"" + XString(field.type).encode(XString.ENCODE_ENTITY) + "\" " : "") +
            ((field.scheme)    ? "scheme=\"" + field.scheme + "\" " : "") +
            ((field.term)      ? "term=\"" + XString(field.term).encode(XString.ENCODE_ENTITY) + "\" " : "") +
            ((field.label)     ? "label=\"" + XString(field.label).encode(XString.ENCODE_ENTITY) + "\" " : "") +
            ((field.title)     ? "title=\"" + XString(field.title).encode(XString.ENCODE_ENTITY) + "\" " : "") +
            ((field.browse)    ? "xfw:onClick=\"" + field.browse + "\" ": "") +
            ((field.hide)      ? "xfw:display=\"hidden\" " : "") +
            ((field.sortOrder) ? "xfw:sortOrder=\"" + field.sortOrder + "\" " : "");
         if (field.attributes)
         {
            for (var j=0; j<field.attributes.length; j++)
            {
               var attrName  = (field.attributes[j].name)  ? field.attributes[j].name  : field.attributes[j][0];
               var attrValue = (field.attributes[j].value) ? field.attributes[j].value : field.attributes[j][1];
               fieldXML += attrName + "=\"" + XString(attrValue).encode(XString.ENCODE_ENTITY) + "\" ";
            }
         }
         if (field.value)
         {
            fieldXML += ">" +
               XString(field.value).encode(XString.ENCODE_ENTITY) +
            "</" + ((field.name) ? field.name : tagName) + ">\n";
         }
         else
            fieldXML += "/>\n";
      }
   }

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.composeLink = function(
   rel,
   ref,
   type
)
{
   rel = (rel == null) ? "alternate" : rel;

   var fieldXML = "";

   fieldXML += "   <link " +
      "rel=\"" + rel + "\" " +
      "href=\"" + XString(ref).encode(XString.ENCODE_ENTITY) + "\" " +
      "type=\"" + type + "\" " +
   "/>\n";

   return fieldXML;
}

//-----------------------------------------------------------------------------

Atom.getEntryXML = function(
   entry,
   addTrackingId,
   addProgress
)
{
   addTrackingId = (addTrackingId == null) ? false : addTrackingId;
   addProgress = (addProgress == null) ? false : addProgress;

   if (!entry.id || entry.id.length == 0)
      entry.id = XUtils.generateId("urn:xcential-com:var:guid:", XUtils.SCHEME_GUID);

   var entryXML = "";

   entryXML += "<entry";
   for (var i=0; i<entry.attributes.length; i++)
      entryXML += " " + entry.attributes[i].name + "=\"" + entry.attributes[i].value + "\"";
   entryXML += ">\n";
   entryXML +=    Atom.composeText(entry.id, "id");
   entryXML +=    Atom.composeStructuredContent(entry.title, "title");
   entryXML +=    Atom.composePerson(entry.author, "author");
   entryXML +=    Atom.composeDate(entry.published, "published");
   entryXML +=    Atom.composeDate(entry.updated, "updated");
   entryXML +=    Atom.composeStructuredContent(entry.summary, "summary");
   entryXML +=    Atom.composeStructuredContent(entry.content, "content");
   entryXML +=    Atom.composeCollection(entry.links, "link");
   entryXML +=    Atom.composeCollection(entry.categories, "category");
   entryXML +=    Atom.composeCollection(entry.customProps);
   entryXML += "</entry>\n";

   return entryXML.replace(/^(.+)$/gm, "   $1"); // Don't add space to empty lines
}

//-----------------------------------------------------------------------------

Atom.slice = function(
   entries,
   startIndex,
   numItems
)
{
   entries = (entries == null) ? [] : entries;
   startIndex = (startIndex == null) ? 0 : startIndex;
   numItems = (numItems == null) ? null : numItems;

   if (entries.length == 0)
      return entries;

   if (numItems != null)
   {
      for (var i=entries.length-1; i>=startIndex+numItems; i--)
         entries.pop();
   }

   if (startIndex > 0)
   {
      for (var i=0; i<startIndex; i++)
         entries.shift();
   }

   return entries;
}

//-----------------------------------------------------------------------------

Atom.extractValue = function(
   field
)
{

   if (field == null)
      return "";

   if (typeof(field) == "string")
      return field;
   else if (field.type == "html")
      return "<![CDATA[" + field.value + "]]>";
   else
      return field.value;

}

//-----------------------------------------------------------------------------

Atom.extractText = function(
   field
)
{

   if (field == null)
      return "";

   if (typeof(field) == "string")
      return field;

   if (field.value == null)
      return "";

   var textValue = field.value.replace(/<\!\[CDATA\[/,"").replace(/\]\]\>/,"");
   switch (field.type)
   {
      case "date":
         return XDate(textValue).toString(XDate.STYLE_SHELL);
      case "text":
         textValue = textValue.replace(/&lt;.*?&gt;/g, " ");
         return textValue;
         break;
      case "html":
         textValue = textValue.replace(/&lt;.*?&gt;/g, " ");
         return textValue.replace(/<[^>]+>/g, " ");
         break;
      case "xhtml":
         textValue = textValue.replace(/&lt;.*?&gt;/g, " ");
         return textValue.replace(/<[^>]+>/g, " ");
         break;
   }

   return textValue;
}

//-----------------------------------------------------------------------------

Atom.extractXDate = function(
   field
)
{

   if (field == null)
      return null;

   if (typeof(field) == "string")
      return XDate(field);

   switch (field.type)
   {
      case "text":
      case "date":
         return XDate(field.value);
         break;
      case "html":
         return XDate(field.value.replace(/<[^>]+>/g, ""));
         break;
      case "xhtml":
         return XDate(field.value.replace(/<[^>]+>/g, ""));
         break;
   }

   return XDate(field.value);
}

//-----------------------------------------------------------------------------

Atom.extractHTML = function(
   field
)
{

   if (field == null)
      return "";

   if (typeof(field) == "string")
      return "<div>" + XString(field).encode(XString.ENCODE_ENTITY) + "</div>";

   switch (field.type)
   {
      case "date":
         return "<div>" + XString(XDate(field.value).toString(XDate.STYLE_SHELL)).encode(XString.ENCODE_ENTITY) + "</div>"
         break;
      case "text":
         return "<div>" + XString(field.value).encode(XString.ENCODE_ENTITY) + "</div>"
         break;
      case "html":
         var html = field.value;
         html = html.replace(/<\!\[CDATA\[/g,"");
         html = html.replace(/\]\]>/g,"")
         return html;
         break;
      case "xhtml":
         var xhtml = field.value;
         xhtml = xhtml.replace(/<\!\[CDATA\[/g,"");
         xhtml = xhtml.replace(/\]\]>/g,"")
         var html = xhtml.replace(/xhtml:/g,"");
         html = html.replace(/\s+xmlns:xhtml=[^>\s]*/g, "");
         return html;
         break;
   }

   return field.value;
}

//-----------------------------------------------------------------------------

Atom.extractXHTML = function(
   field
)
{

   if (field == null)
      return "";

   if (typeof(field) == "string")
      return "<xhtml:div xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">" + field + "</xhtml:div>";

   switch (field.type)
   {
      case "text":
         var text = field.value;
         var xhtml = "<xhtml:div xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">" + text + "</xhtml:div>";
         return xhtml;
         break;
      case "html":
         var html = field.value;
         html = html.replace(/^\s*<\!\[CDATA\[/,"");
         html = html.replace(/\]\]>\s*$/,"");
         var xhtml = "<xhtml:div xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"><![CDATA[" + html + "]]></xhtml:div>";
         return xhtml;
         break;
      case "xhtml":
         return field.value;
         break;
   }

   return field.value;
}

//=============================================================================
// Public Interface

Atom.prototype.getTitle = function(
   title
)
{
   title = (title == null) ? null : title;

   var feed = this.valueOf();

   feed.title = (title) ? title : feed.title;
   feed.subtitle = (title) ? null : feed.subtitle;

   var title = XApp.APPLICATION_NAME_TRADE;
   title += (XString(feed.title).isSomething()) ? " - " + feed.title : "";
   title += (XString(feed.subtitle).isSomething()) ? " - " + feed.subtitle : "";

   return title;
}

//-----------------------------------------------------------------------------

Atom.prototype.toXML = function()
{

   var feed = this.valueOf();

   var id = (XString(feed.id).isSomething()) ? feed.id : XUtils.generateId("urn:xcential-com:var:guid:", XUtils.SCHEME_GUID);

   // Build the results XML document
   var xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";

   xml += "<feed " +
      "xmlns=\"http://www.w3.org/2005/Atom\" " +
      "xmlns:opensearch=\"http://a9.com/-/spec/opensearch/1.1/\" " +
      "xmlns:xfw=\"http://www.xcential.com/schemas/2006/xfw\" " +
      "xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" " +
      ((feed.customProps && feed.customProps.namespaces) ? feed.customProps.namespaces + " " : "") +
      "xml:lang=\"en-us\" " +
   ">\n";

   xml += Atom.composeStructuredContent(feed.title, "title");
   xml += Atom.composeStructuredContent(feed.subtitle, "subtitle");
   xml += Atom.composeText(id, "id");
   xml += Atom.composeURL(feed.icon, "icon");
   xml += Atom.composeURL(feed.logo, "logo");
   xml += Atom.composeDate(XDate().toString(), "updated");
   xml += Atom.composePerson(feed.author, "author");
   xml += Atom.composeStructuredContent(feed.copyright, "rights");
   xml += Atom.composeText(feed.generator, "generator");

   // OpenSearch fields
   xml += Atom.composeText(feed.totalResults, "opensearch:totalResults");
   xml += Atom.composeText(feed.itemsPerPage, "opensearch:itemsPerPage");
   xml += Atom.composeText(feed.startIndex, "opensearch:startIndex");

   xml += (feed.searchTerms != null) ?  "   <opensearch:Query role=\"request\" searchTerms=\"" + XString(feed.searchTerms).encode(XString.ENCODE_ENTITY) + "\" startPage=\"1\" />\n" : "";
   xml += (feed.searchTerms != null) ?  "   <link rel=\"search\" type=\"application/opensearchdescription+xml\" href=\"" + XApp.URL_OPEN_SEARCH_DESCRIPTION + "\"/>\n" : "";

   // Paging navigation
   if (feed.selfRef != null)
   {
      xml += Atom.composeLink("self", XRef(feed.selfRef).addArgs(this.linkCustomArgs) + "&format=atom", Atom.MEDIA_TYPE);

      var link = feed.selfRef;
      link = link.replace(/totalResults=[^\&]*/i,"");
      link = link.replace(/itemsPerPage=[^\&]*/i,"");
      link = link.replace(/startIndex=[^\&]*/i,"");
      link = link.replace(/format=[^\&]*/i,"");

      var baseArgs = (this.linkCustomArgs) ? this.linkCustomArgs : "";
      firstArgs += "&searchId=" + encodeURIComponent(id);
      firstArgs += "&totalResults=" + feed.totalResults;
      firstArgs += "&itemsPerPage=" + feed.itemsPerPage;

      if (feed.startIndex != null && feed.startIndex > 1)
      {
         var firstArgs = baseArgs;
         firstArgs += "&startIndex=" + "1";
         firstArgs += "&format=atom";
         xml += Atom.composeLink("first", XRef(link).addArgs(firstArgs), Atom.MEDIA_TYPE);
      }
      if (feed.startIndex != null && feed.startIndex > 1)
      {
         var previousStartIndex = feed.startIndex - feed.itemsPerPage;
         var previousArgs = baseArgs;
         previousArgs += "&startIndex=" + ((previousStartIndex > 0) ? previousStartIndex : 1);
         previousArgs += "&format=atom";
         xml += Atom.composeLink("previous", XRef(link).addArgs(previousArgs), Atom.MEDIA_TYPE);
      }
      if (feed.startIndex != null && feed.itemsPerPage != null && ((feed.startIndex + feed.itemsPerPage - 1) < feed.totalResults))
      {
         var nextStartIndex = feed.startIndex + feed.itemsPerPage;
         var nextArgs = baseArgs;
         nextArgs += "&startIndex=" + nextStartIndex;
         nextArgs += "&format=atom";
         xml += Atom.composeLink("next", XRef(link).addArgs(nextArgs), Atom.MEDIA_TYPE);
      }
      if (feed.startIndex != null && feed.itemsPerPage != null && ((feed.startIndex + feed.itemsPerPage - 1) < feed.totalResults))
      {
         var lastStartIndex = Math.floor((feed.totalResults-1)/feed.itemsPerPage) * feed.itemsPerPage + 1;
         var lastArgs = baseArgs;
         lastArgs += "&startIndex=" + lastStartIndex;
         lastArgs += "&format=atom";
         xml += Atom.composeLink("last", XRef(link).addArgs(lastArgs), Atom.MEDIA_TYPE);
      }
   }

   xml += Atom.composeCollection(feed.links, "link");
   xml += Atom.composeCollection(feed.categories, "category");
   xml += Atom.composeCollection(feed.customProps);

   // Add all entries
   for (var i=0; i<feed.entries.length; i++)
      xml += Atom.getEntryXML(feed.entries[i]);

   xml += "</feed>\n";

   return xml;
}

//=============================================================================
