home *** CD-ROM | disk | FTP | other *** search
- /* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is NewsFox.
- *
- * The Initial Developer of the Original Code is
- * Andy Frank <andy@andyfrank.com>.
- * Portions created by the Initial Developer are Copyright (C) 2005-2007
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Andrey Gromyko <andrey@gromyko.name>
- * Ron Pruitt <wa84it@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the LGPL or the GPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
- ////////////////////////////////////////////////////////////////
- // Global
- ////////////////////////////////////////////////////////////////
-
- // common to many *.js
- var gFmodel = new FdModel();
- var gIdx = new Indexes();
- var gFdGroup = new Array();
- var gCollect = null;
-
- // globals just in model.js
- const FOLDER_OPEN = "chrome://newsfox/skin/images/folderOpen.png";
- const FOLDER_CLOSED = "chrome://newsfox/skin/images/folderClosed.png";
- const FOLDER_SEARCH = "chrome://newsfox/skin/images/folderSearch.png";
- const ICON_ERR = "chrome://newsfox/skin/images/brokenFeed.png";
-
- var dragLevel = 3;
- var dragGrp = -1;
-
- ////////////////////////////////////////////////////////////////
- // Model
- ////////////////////////////////////////////////////////////////
-
- function FdModel()
- {
- this.add = function(feed,isExcluded)
- {
- if (isExcluded)
- feeds.push(feed);
- else
- feeds.splice(gFmodel.size(),0,feed);
- }
- this.get = function(index) { return feeds[index]; }
- this.set = function(index, feed)
- {
- if(index < 0 || index > feeds.length - 1)
- return;
- feeds[index] = feed;
- }
- this.getIndexByURL = function(url)
- {
- for(var i=0; i < feeds.length; i++)
- if(url == feeds[i].url)
- return i;
- return -1;
- }
- this.getFeedByURL = function(url)
- {
- var nFeed = this.getIndexByURL(url);
- if (nFeed > -1) return feeds[nFeed];
- else return null;
- }
-
- this.size = function() // returns size without excluded feed(s)
- {
- var size = 0;
- for( var i = 0; i < feeds.length; i++ )
- if( !feeds[i].exclude )
- size++;
- return size;
- }
-
- this.sizeTotal = function() // returns total size, i.e. with excluded from seeing feed(s)
- {
- return feeds.length;
- }
-
- var feeds = new Array();
-
- /**
- * Return a new unique uid.
- */
- this.makeUniqueUid = function(url)
- {
- var index = url.indexOf("://");
- var body;
- if( index > -1 )
- if (url.charAt(index+3) == "/") // file:///
- body = "local";
- else
- body = url.substring(index+3);
- else
- body = url;
- var domain = body;
- if( body.indexOf("/") != -1 )
- domain = body.split("/")[0];
- if( domain.indexOf(":") != -1 )
- domain = domain.split(":")[0]; // there are cases when port number follows domain name
- var count = 1;
- var name = domain;
- for (var i=0; i<feeds.length; i++)
- {
- if (name == feeds[i].uid)
- {
- name = domain + (count++);
- i = -1; // reset loop
- }
- }
- return name;
- }
-
- /**
- * Remove the given feed from the model.
- */
- this.remove = function(feed)
- {
- for (var i=0; i < feeds.length; i++)
- if (feed.uid == feeds[i].uid)
- {
- feeds.splice(i,1);
- return;
- }
- }
- }
-
- ////////////////////////////////////////////////////////////////
- // Group Model
- ////////////////////////////////////////////////////////////////
-
- function FeedGroup()
- {
- const NF_SB = document.getElementById("newsfox-string-bundle");
- this.title = NF_SB.getString('newGroup');
- this.expanded = false;
- this.list = new Array();
- this.search = false;
- this.srchdat = { flagged : 2, unread : 2, text : "", textflags : 6, startTime : -1, endTime: 0 };
- this.getUnread = function()
- {
- if (this.search) return 0; // TODO, simple on the fly doesn't work well
- var unread = 0;
- for (var i=0; i<this.list.length; i++)
- unread += gFmodel.get(this.list[i]).getUnread(0);
- return unread;
- }
- }
-
- function Indexes()
- {
- this.fdgp = new Array();
- this.feed = new Array();
- this.catg = new Array();
- this.open = new Array();
- }
-
- ////////////////////////////////////////////////////////////////
- // Feed Model
- ////////////////////////////////////////////////////////////////
-
- function Feed()
- {
- this.uid = null;
- this.url = null;
- this.homepage = null;
- this.icon = new Image;
- this.icon.src = ICON_OK;
- this.defaultName = null;
- this.customeName = null;
- this.error = ERROR_OK;
- this.loaded = false;
- this.deleteOld = true;
- this.dontDeleteUnread = true;
- this.daysToKeep = 0;
- this.autoCheck = true;
- var categories = new Array();
- this.exclude = false;
-
- // 0 Use global setting
- // 1 Override to show as text
- // 2 Override to show as webpage
- // 3 Override to show as newspaper
- this.style = 0;
- this.getStyle = function()
- {
- if (this.style == 0)
- return gOptions.globalStyle;
- return this.style;
- }
-
- /**
- * Return display name for feed.
- */
- this.getDisplayName = function()
- {
- return ((this.customName != null) && (this.customName.length > 0)) ? this.customName : this.defaultName;
- }
-
- this.setDefaultName = function(dname) { this.defaultName = dname; }
-
- /**
- * Return the number of unread items for this feed.
- */
- this.getUnread = function(nCategory)
- {
- var unread = 0;
- for (var i=0; i<this.flags.length; i++)
- if ((this.flags[i] & 0x01) == 0) // unread
- {
- if( nCategory == 0 )
- unread++;
- else
- {
- var ScatnameS = "\/" + categories[nCategory-1] + "\/";
- var ScatS = "\/" + articles[i].category + "\/";
- if (ScatS.indexOf(ScatnameS) > -1)
- unread++;
- }
- }
- return unread;
- }
-
- this.add = function(article,flag)
- {
- articles.push(article);
- if(article.category != "")
- this.addCategory(article.category);
- this.flags.push(flag);
- }
-
- this.deletedAdd = function(article) { deletedarticles.push(article); }
-
- this.get = function(index) { return articles[index]; }
-
- this.deletedget = function(index) { return deletedarticles[index]; }
-
- this.set = function(index,item) { articles[index] = item; }
-
- this.remove = function(index)
- {
- articles.splice(index,1);
- this.flags.splice(index,1);
- }
-
- this.deletedremove = function(index) { deletedarticles.splice(index,1); }
-
- this.getIndexById = function(id)
- {
- for(var i=0; i < articles.length; i++)
- if(id == articles[i].id)
- return i;
- return -1;
- }
-
- this.deleteById = function(id)
- {
- if (id == null) return;
- var index = this.getIndexById(id);
- if( index >= 0 && index < articles.length)
- {
- deletedarticles.push(articles[index]);
- articles.splice(index,1);
- this.flags.splice(index,1);
- }
- }
-
- this.size = function() { return articles.length; }
- this.deletedsize = function() { return deletedarticles.length; }
- var articles = new Array();
- var deletedarticles = new Array();
-
- // Flags
- this.flags = new Array();
-
- this.isRead = function(index)
- {
- return ((this.flags[index] & 0x01) != 0);
- }
- this.setRead = function(index, value)
- {
- if (value) this.flags[index] |= 0x01;
- else this.flags[index] &= 0xFE;
- }
-
- this.isFlagged = function(index)
- {
- return ((this.flags[index] & 0x04) != 0);
- }
- this.setFlagged = function(index, value)
- {
- if (value) this.flags[index] |= 0x04;
- else this.flags[index] &= 0xFB;
- }
-
- // AG: added categories
- this.addCategory = function(category)
- {
- var catArray = category.split("\/");
- for (var i=0; i<catArray.length; i++)
- {
- var exists = false;
- for (var j=0; j<categories.length; j++)
- if (catArray[i] == categories[j]) exists = true;
- if (!exists) categories.push(catArray[i]);
- }
- }
- this.getCategories = function() { return categories; }
- this.sortCategories = function() { categories.sort(); }
- }
-
- ////////////////////////////////////////////////////////////////
- // FeedTree Model
- ////////////////////////////////////////////////////////////////
-
- function dragIt(evt) {
- var tree = evt.target.parentNode;
- if (tree.id != "newsfox.feedTree") return;
- var row = {}, col = {}, type = {};
- tree.treeBoxObject.getCellAt(evt.clientX, evt.clientY, row, col, type);
- dragLevel = tree.view.getLevel(row.value);
- dragGrp = gIdx.fdgp[row.value];
- if (dragLevel <= 1) nsDragAndDrop.startDrag(evt,feedtreeObserver);
- }
-
- var feedtreeObserver = {
-
- onDragStart: function (evt,transferData,action)
- {
- var row = {}, col = {}, type = {};
- evt.target.parentNode.treeBoxObject.getCellAt(evt.clientX, evt.clientY, row, col, type);
- transferData.data=new TransferData();
- transferData.data.addDataForFlavour("feedtreerow",gIdx.feed[row.value]);
- },
-
- getSupportedFlavours : function ()
- {
- var flavours = new FlavourSet();
- flavours.appendFlavour("feedtreerow");
- return flavours;
- },
-
- onDragOver: function (evt,flavour,session){},
-
- onDrop: function (evt,dropdata,session){}
- };
-
- function FeedTreeModel()
- {
- // 3/25/05 - Temp disable flag/trash folder
- // AG: added category. Most subcode is reworked
- // 11 Jan 07 RP: remove flag/trash, add groups, code reworked
-
- this.rowCount = gIdx.fdgp.length;
- this.isTopLevelElement = function(row){ return (this.getLevel(row) == 0); }
- this.getCellText = function(row,col)
- {
- var level = this.getLevel(row);
- var text, unread;
- if (level == 0)
- {
- var curFdGroup = gFdGroup[gIdx.fdgp[row]];
- text = curFdGroup.title;
- unread = curFdGroup.getUnread();
- if (row == 0) newTitle(unread);
- }
- else
- {
- var feed = gFmodel.get(gIdx.feed[row]);
- var nCategory = gIdx.catg[row];
- text = "";
- unread = feed.getUnread(nCategory);
- if( nCategory )
- {
- var categories = feed.getCategories();
- text = categories[nCategory-1];
- }
- else
- text = feed.getDisplayName();
- }
- if (unread > 0) text += " (" + unread + ")";
- return text;
- }
- this.setTree = function(treebox){ this.treebox = treebox; }
- this.isContainer = function(row){ return (this.getLevel(row) < 2); }
- this.isContainerEmpty = function(row)
- {
- var level = this.getLevel(row);
- if (level == 0 && gFdGroup[gIdx.fdgp[row]].search) return true;
- var feed = gFmodel.get(gIdx.feed[row]);
- if (level == 0 && gFdGroup[gIdx.fdgp[row]].list.length) return false;
- if (level == 1 && feed.loaded && feed.getCategories().length) return false;
- return true;
- }
- this.isContainerOpen = function(row){ return gIdx.open[row]; }
- this.hasNextSibling = function(row, index)
- {
- if (index+1 > gIdx.fdgp.length-1) return false;
- var level = this.getLevel(row);
- if (level == 0) return (gIdx.fdgp[gIdx.fdgp.length-1] != gIdx.fdgp[row]);
- else if (level == 1) return (gIdx.fdgp[index+1] == gIdx.fdgp[row]);
- else return ((gIdx.fdgp[index+1] == gIdx.fdgp[row]) && (gIdx.feed[index+1] == gIdx.feed[row]));
- }
- this.getParentIndex = function(row)
- {
- var index = row;
- var level = this.getLevel(row);
- if (level == 0) index = -2;
- else if (level == 1)
- while (gIdx.fdgp[index] == gIdx.fdgp[row]) index--;
- else
- while (gIdx.feed[index] == gIdx.feed[row]) index--;
- return ++index;
- }
- this.isSeparator = function(row){ return false; }
- this.isEditable = function(row,col){ return false; }
- this.isSorted = function(row){ return false; }
- this.getLevel = function(row)
- {
- var level = 0;
- if (gIdx.catg[row] > 0) level = 2;
- else if (gIdx.feed[row] > -1) level = 1;
- return level;
- }
- this.getImageSrc = function(row,col)
- {
- var retval = null;
- var level = this.getLevel(row);
- if (level == 0)
- {
- if (this.isContainerOpen(row))
- retval = FOLDER_OPEN;
- else if (gFdGroup[gIdx.fdgp[row]].search)
- retval = FOLDER_SEARCH;
- else
- retval = FOLDER_CLOSED;
- }
- else if (level == 1)
- {
- var feed = gFmodel.get(gIdx.feed[row]);
- var nofeederror = (feed.error == ERROR_OK);
- retval = nofeederror ? feed.icon.src : ICON_ERR;
- }
- return retval;
- }
- this.getRowProperties = function(row,props) {}
- this.getCellProperties = function(row,col,props)
- {
- if (gIdx.catg[row] == 0)
- {
- var aserv = Components.classes["@mozilla.org/atom-service;1"].
- getService(Components.interfaces.nsIAtomService);
- props.AppendElement(aserv.getAtom("faviconcol"));
- }
- var hasUnread = false;
- var nFeed = gIdx.feed[row];
- if (nFeed == -1 && gFdGroup[gIdx.fdgp[row]].getUnread() > 0)
- hasUnread = true;
- if (nFeed > -1 && gFmodel.get(nFeed).getUnread(gIdx.catg[row]) > 0)
- hasUnread = true;
- if (hasUnread)
- {
- var aserv = Components.classes["@mozilla.org/atom-service;1"].
- getService(Components.interfaces.nsIAtomService);
- props.AppendElement(aserv.getAtom("unread"));
- }
- }
- this.getColumnProperties = function(colid,col,props){}
- this.toggleOpenState = function(row)
- {
- if (this.isContainerEmpty(row)) return;
- var feedtree = document.getElementById("newsfox.feedTree");
- var fRow = feedtree.treeBoxObject.getFirstVisibleRow();
- var level = this.getLevel(row);
- if (level == 0)
- {
- var curGrp = gIdx.fdgp[row];
- var grp = gFdGroup[curGrp];
- if (grp.expanded)
- {
- var num = 0;
- while (gIdx.fdgp[row+num] == curGrp) num++;
- num--;
- gIdx.fdgp.splice(row+1,num);
- gIdx.feed.splice(row+1,num);
- gIdx.catg.splice(row+1,num);
- gIdx.open.splice(row+1,num);
- feedtree.treeBoxObject.rowCountChanged(row+1,-num);
- }
- else
- {
- for (var i=1; i <= grp.list.length; i++)
- {
- gIdx.fdgp.splice(row+i,0,curGrp);
- gIdx.feed.splice(row+i,0,grp.list[i-1]);
- gIdx.catg.splice(row+i,0,0);
- gIdx.open.splice(row+i,0,false);
- }
- feedtree.treeBoxObject.rowCountChanged(row+1,grp.list.length);
- }
- grp.expanded = !grp.expanded;
- gIdx.open[row] = !gIdx.open[row];
- }
- else if (level == 1)
- {
- var curFeed = gIdx.feed[row];
- var feed = gFmodel.get(curFeed);
- if (gIdx.open[row])
- {
- var num = 0;
- while (gIdx.feed[row+num] == curFeed) num++; // can't use categories.length since # categories may change
- num--;
- gIdx.fdgp.splice(row+1,num);
- gIdx.feed.splice(row+1,num);
- gIdx.catg.splice(row+1,num);
- gIdx.open.splice(row+1,num);
- feedtree.treeBoxObject.rowCountChanged(row+1,-num);
- }
- else
- {
- for (var i=1; i <= feed.getCategories().length; i++)
- {
- gIdx.fdgp.splice(row+i,0,gIdx.fdgp[row]);
- gIdx.feed.splice(row+i,0,gIdx.feed[row]);
- gIdx.catg.splice(row+i,0,i);
- gIdx.open.splice(row+i,0,false);
- }
- feedtree.treeBoxObject.rowCountChanged(row+1,feed.getCategories().length);
- }
- gIdx.open[row] = !gIdx.open[row];
- }
- else return;
- saveModels();
- feedtree.treeBoxObject.scrollToRow(fRow);
- }
- this.canDrop = function(row,orientation)
- {
- // if (orientation == 0) return false;
- switch (this.getLevel(row))
- {
- case 0:
- return (dragLevel == 0) ? true : false;
- case 1:
- return (dragLevel == 1 && dragGrp == gIdx.fdgp[row]) ? true : false;
- case 2:
- default:
- return false;
- }
- }
- this.drop = function(row,orientation)
- {
- var flavourSet = feedtreeObserver.getSupportedFlavours();
- var transferData = nsTransferable.get(flavourSet, nsDragAndDrop.getDragData, true);
- var dropdata = transferData.first.first;
- var oldFeed = dropdata.data;
- var newGrp = gIdx.fdgp[row] + (orientation == 1);
- var newFeed = gIdx.feed[row];
- if (orientation == 1)
- {
- var i = row;
- while (gIdx.feed[i] == newFeed) i++;
- if (gIdx.fdgp[i] == dragGrp) newFeed = gIdx.feed[i]
- else newFeed = -2;
- }
- if (dragLevel == 0) mvGrp(dragGrp,newGrp);
- else // dragLevel == 1
- mvFeed(dragGrp,oldFeed,newFeed);
- dragLevel = 3;
- dragGrp = -1;
- }
- }
-
- ////////////////////////////////////////////////////////////////
- // Article Model
- ////////////////////////////////////////////////////////////////
-
- function Article()
- {
- this.link = null;
- this.title = "";
- this.body = "";
- this.date = null;
- // AG: added category
- this.category = "";
- this.enclosures = new Array();
- this.id = null;
- this.toRemove = false;
- }
-
- function Enclosure()
- {
- this.url = "";
- this.type = "";
- this.length = "";
- }
-
- ////////////////////////////////////////////////////////////////
- // ArticleTree Model
- ////////////////////////////////////////////////////////////////
-
- function ArticleTreeModel()
- {
- var flagIcon = "chrome://newsfox/skin/images/flag.png";
- var readIcon = "chrome://newsfox/skin/images/read.png";
- var unreadIcon = "chrome://newsfox/skin/images/unread.png";
-
- removeHeaderArrows();
-
- this.rowCount = gCollect.size();
- this.getCellText = function(row,col)
- {
- // Try to handle both Firefox 1.0 and 1.1
- var colId = (col.id) ? col.id : col;
- switch (colId)
- {
- case "title": return entityDecode(gCollect.get(row).title);
- case "date": return displayDate(gCollect.get(row).date, gOptions.dateStyle);
- default: return "debug-" + col;
- }
- }
- this.setTree = function(treebox){ this.treebox = treebox; }
- this.isContainer = function(row){ return false; }
- this.isSeparator = function(row){ return false; }
- this.isSorted = function(row){ return false; }
- this.getLevel = function(row){ return 0; }
- this.getImageSrc = function(row,col)
- {
- var read = gCollect.isRead(row);
- var flag = gCollect.isFlagged(row);
-
- // Try to handle both Firefox 1.0 and 1.1
- var colId = (col.id) ? col.id : col;
- switch (colId)
- {
- case "read": return read ? readIcon : unreadIcon;
- case "flag": return flag ? flagIcon : readIcon;
- case "title": return (gCollect.type == 0 || gCollect.type == 3) ? gCollect.getFeed(row).icon.src : null;
- default: return null;
- }
- }
- this.getRowProperties = function(row,props) {}
- this.getCellProperties = function(row,col,props)
- {
- // Try to handle both Firefox 1.0 and 1.1
- var colId = (col.id) ? col.id : col;
- if (colId == "title" && (gCollect.type == 0 || gCollect.type == 3))
- {
- var aserv = Components.classes["@mozilla.org/atom-service;1"].
- getService(Components.interfaces.nsIAtomService);
- props.AppendElement(aserv.getAtom("faviconcol"));
- }
- if (!gCollect.isRead(row))
- {
- var aserv = Components.classes["@mozilla.org/atom-service;1"].
- getService(Components.interfaces.nsIAtomService);
- props.AppendElement(aserv.getAtom("unread"));
- }
- }
- this.getColumnProperties = function(colid,col,props) {}
- this.cycleHeader = function(col,elem)
- {
- // Try to handle both Firefox 1.0 and 1.1
- var colId = (col.id) ? col.id : col;
-
- var colObj = document.getElementById(colId);
- var direction = colObj.getAttribute("sortDirection");
- removeHeaderArrows();
- var newDir;
- switch (colId)
- {
- case "flag":
- case "title":
- newDir = "ascending";
- if (direction == "ascending") newDir = "descending";
- break;
- case "read":
- case "date":
- newDir = "descending";
- if (direction == "descending") newDir = "ascending";
- }
- var arttree = document.getElementById("newsfox.articleTree");
- arttree.blur();
- arttree.view.selection.select(-1);
- gCollect.artSort(colId,newDir);
- if (gCollect.type == 1) // feed, not group or category
- {
- saveFeed(gCollect.feed); // feed(s) not changed by resorting
- saveFeedModel(); // group or category
- }
- colObj.setAttribute("sortDirection", newDir);
- }
- this.cycleCell = function(row,col)
- {
- // Try to handle both Firefox 1.0 and 1.1
- var colId = (col.id) ? col.id : col;
-
- if (colId == "read")
- {
- var read = gCollect.isRead(row);
- gCollect.setRead(row, !read);
- var tree = document.getElementById("newsfox.feedTree");
- tree.treeBoxObject.invalidate();
- }
- else if (colId == "flag")
- {
- var flag = gCollect.isFlagged(row);
- gCollect.setFlagged(row, !flag);
- }
- var arttree = document.getElementById("newsfox.articleTree");
- arttree.treeBoxObject.invalidate();
- }
- }
-
- function removeHeaderArrows()
- {
- var flagObj = document.getElementById("flag");
- var titleObj = document.getElementById("title");
- var readObj = document.getElementById("read");
- var dateObj = document.getElementById("date");
- flagObj.setAttribute("sortDirection", "natural");
- titleObj.setAttribute("sortDirection", "natural");
- readObj.setAttribute("sortDirection", "natural");
- dateObj.setAttribute("sortDirection", "natural");
- }
-
- ////////////////////////////////////////////////////////////////
- // Collections
- ////////////////////////////////////////////////////////////////
-
- // AG: categoryNo isn't an index. 0 means root (or whole feed)
- function NormalCollection(index, categoryNo, isDisplayed)
- {
- this.type = 1; // feed
- if (categoryNo > 0) this.type = 2;
- var feed = gFmodel.get(index);
- this.feed = feed;
- if (isDisplayed)
- {
- var title = feed.getDisplayName();
- var hasHomepage = (feed.homepage != "");
- setFeedbarButtons(this.type,title,hasHomepage);
- }
- var items = new Array();
- var itemFeed = new Array();
- var itemIndex = new Array();
- var art;
- if (categoryNo > 0)
- {
- var categories = feed.getCategories();
- var ScatnameS = "\/" + categories[categoryNo - 1] + "\/";
- }
- for (var i=0; i<feed.size(); i++)
- if (categoryNo < 1)
- {
- items.push(feed.get(i));
- itemFeed.push(index);
- itemIndex.push(i);
- }
- else
- {
- art = feed.get(i);
- var ScatS = "\/" + art.category + "\/";
- if(ScatS.indexOf(ScatnameS) > -1)
- {
- items.push(art);
- itemFeed.push(index);
- itemIndex.push(i);
- }
- }
- this.get = function(index) { return items[index]; }
- this.size = function() { return items.length; }
- this.isRead = function(row) { return feed.isRead(itemIndex[row]); }
- this.setRead = function(row,value) { feed.setRead(itemIndex[row],value); }
- this.isFlagged = function(row) { return feed.isFlagged(itemIndex[row]); }
- this.setFlagged = function(row,value)
- { feed.setFlagged(itemIndex[row],value); }
- this.getFeed = function(index) { return feed; }
- this.artSort = function(by,dir)
- { artSort(by,dir,feed,items,itemFeed,itemIndex,this.type); }
- }
-
- function GroupCollection(grpindex, isSearch)
- {
- const NF_SB = document.getElementById("newsfox-string-bundle");
- var srchitem = new Array();
- this.grpindex = grpindex;
- var grp = gFdGroup[grpindex];
- var grpHeading;
- if (isSearch)
- {
- this.type = 3; // search
- grpHeading = NF_SB.getString('srchName');
- var srchstr = mkSrchstr(grp.srchdat,srchitem);
- }
- else
- {
- this.type = 0; // group
- grpHeading = NF_SB.getString('grpName');
- }
- var title = grpHeading + " " + grp.title;
- setFeedbarButtons(this.type,title,false);
- var items = new Array();
- var itemIndex = new Array();
- var itemFeed = new Array();
- var art, feed, nFeed;
- for (var i=0; i<grp.list.length; i++)
- {
- nFeed = grp.list[i];
- feed = gFmodel.get(nFeed);
- for (var j=0; j<feed.size(); j++)
- if (this.type == 0 || hasProp(j, feed, grp.srchdat, srchstr, srchitem))
- {
- items.push(feed.get(j));
- itemIndex.push(j);
- itemFeed.push(nFeed);
- }
- }
-
- this.artSort = function(by,dir)
- { artSort(by,dir,feed,items,itemFeed,itemIndex,this.type); }
-
- this.artSort("date","descending");
-
- this.get = function(index) { return items[index]; }
- this.size = function() { return items.length; }
- this.isRead = function(row) { return gFmodel.get(itemFeed[row]).isRead(itemIndex[row]); }
- this.setRead = function(row,value) { gFmodel.get(itemFeed[row]).setRead(itemIndex[row],value); }
- this.isFlagged = function(row) { return gFmodel.get(itemFeed[row]).isFlagged(itemIndex[row]); }
- this.setFlagged = function(row,value)
- { gFmodel.get(itemFeed[row]).setFlagged(itemIndex[row],value); }
- this.getFeed = function(index) { return gFmodel.get(itemFeed[index]); }
- this.getSrchText = function() { return srchitem; }
- this.getTitle = function() { return title; }
- }
-
- function EmptyCollection()
- {
- var items = new Array();
- this.type = -1; // empty
- setFeedbarButtons(this.type,"?",false);
- this.size = function() { return items.length; }
- }
-
- ////////////////////////////////////////////////////////////
- ///// Collection utilities
- ///////////////////////////////////////////////////////////
-
- function hasProp(j, feed, srchdat, srchstr, st)
- {
- if ((srchdat.flagged != 2) && (srchdat.flagged == feed.isFlagged(j)))
- return false;
- if ((srchdat.unread != 2) && (srchdat.unread != feed.isRead(j)))
- return false;
- var art = feed.get(j);
-
- var now = new Date().getTime();
- var artTime = art.date.getTime();
- if (artTime > now - srchdat.endTime) return false;
- if (srchdat.startTime > 0 && now - srchdat.startTime > artTime) return false;
-
- var caseSen = ((srchdat.textflags & 0x04) == 0);
- var matchWhat = (srchdat.textflags & 0x03) + 1;
- var arttext = "";
- arttext += ((matchWhat & 0x01) != 0) ? art.body : "";
- arttext += ((matchWhat & 0x02) != 0) ? art.title : "";
- if (!caseSen)
- arttext = arttext.toLowerCase();
- return eval(srchstr);
- }
-
- function srchText(srchtext, arttext)
- {
- return (arttext.indexOf(srchtext) != -1);
- }
-
- function mkSrchstr(srchdat,st)
- {
- var caseSen = ((srchdat.textflags & 0x04) == 0);
- var srchtext = srchdat.text;
- if (srchtext == "") return "true";
- if (!caseSen)
- srchtext = srchtext.toLowerCase();
-
- var srchstr = srchtext;
- var i = 0;
- // preprocess quoted stuff
- var postext = 0;
- var done = false;
- var malformed = false;
- var item, repl, newrepl;
- while (!done)
- {
- var q1 = srchtext.indexOf("'",postext);
- var q2 = srchtext.indexOf('"',postext);
- if (q1 == -1 && q2 == -1) done = true;
- else if ((q1 > -1 && q1 < q2) || q2 == -1)
- {
- var q3 = srchtext.indexOf("'",q1+1);
- if (q3 == -1) // unbalanced error
- {
- malformed = true;
- srchtext += "'";
- srchstr += "'";
- q3 = srchtext.indexOf("'",q1+1);
- }
- item = srchtext.substring(q1+1,q3);
- newrepl = '"st[' + i + ']"';
- st[i++] = item;
- repl = "'" + item + "'";
- srchstr = srchstr.replace(repl,newrepl);
- postext = q3+1;
- }
- else // -1 < q2 < q1 || q1 == -1
- {
- var q3 = srchtext.indexOf('"',q2+1);
- if (q3 == -1) // unbalanced error
- {
- malformed = true;
- srchtext += '"';
- srchstr += '"';
- q3 = srchtext.indexOf('"',q2+1);
- }
- item = srchtext.substring(q2+1,q3);
- newrepl = '"st[' + i + ']"';
- st[i++] = item;
- repl = '"' + item + '"';
- srchstr = srchstr.replace(repl,newrepl);
- postext = q3+1;
- }
- }
-
- // process string
- postext = 0;
- var pos;
- done = false;
- var inParen = 0;
- var nextIsItem = true;
- while (postext < srchstr.length)
- {
- switch (srchstr.charAt(postext))
- {
- case '"':
- //alert("case\"");
- postext = srchstr.indexOf('"',postext+1)+1;
- nextIsItem = false;
- break;
- case "-":
- //alert("case-");
- if (srchstr.charAt(postext+1) != ' ' && srchstr.charAt(postext+1) != '|')
- {
- srchstr = srchstr.substring(0,postext) + "!" + srchstr.substring(postext+1);
- postext++;
- }
- else
- {
- if (nextIsItem)
- {
- malformed = true;
- srchstr = srchstr.substring(0,postext+1) + srchstr.substring(postext+2);
- }
- else
- {
- malformed = true;
- srchstr = srchstr.substring(0,postext) + srchstr.substring(postext+1);
- }
- }
- break;
- case "(":
- //alert("case(");
- inParen++;
- postext++;
- break;
- case ")":
- //alert("case)");
- if (inParen > 0 && !nextIsItem)
- {
- inParen--;
- postext++;
- }
- else
- {
- malformed = true;
- srchstr = srchstr.substring(0,postext) + srchstr.substring(postext+1);
- }
- break;
- case "|":
- //alert("case|");
- if (!nextIsItem)
- {
- srchstr = srchstr.substring(0,postext) + " || " + srchstr.substring(postext+1);
- postext += 4;
- nextIsItem = true;
- }
- else
- {
- malformed = true;
- srchstr = srchstr.substring(0,postext) + srchstr.substring(postext+1);
- }
- break;
- case " ":
- //alert("case-space");
- pos = postext;
- while (srchstr.charAt(pos) == " ") pos++;
- switch (srchstr.charAt(pos))
- {
- case "|":
- case ")":
- srchstr = srchstr.substring(0,postext) + srchstr.substring(pos);
- break;
- case "o":
- case "O":
- if (srchstr.charAt(pos+1).toLowerCase() == "r" && srchstr.charAt(pos+2) == " ")
- {
- srchstr = srchstr.substring(0,postext) + "|" + srchstr.substring(pos+3);
- break;
- }
- default:
- if (pos >= srchstr.length)
- {
- postext = pos;
- break;
- }
- if (nextIsItem)
- srchstr = srchstr.substring(0,postext) + srchstr.substring(pos);
- else
- {
- srchstr = srchstr.substring(0,postext) + " && " + srchstr.substring(pos);
- postext += 4;
- nextIsItem = true;
- }
- }
- break;
- default: // search item
- //alert("case");
- if (nextIsItem)
- {
- pos = postext;
- while (srchstr.charAt(pos) != '|' && srchstr.charAt(pos) != ' ' && srchstr.charAt(pos) != ')' && pos < srchstr.length) pos++;
- if (srchstr.charAt(pos) == ')' && inParen <= 0)
- while (srchstr.charAt(pos) != '|' && srchstr.charAt(pos) != ' ' && pos < srchstr.length) pos++;
- item = srchstr.substring(postext,pos);
- newrepl = '"st[' + i + ']"';
- st[i++] = item;
- srchstr = srchstr.replace(item,newrepl);
- postext = srchstr.indexOf('"',postext+1)+1;
- nextIsItem = false;
- }
- else
- {
- malformed = true;
- srchstr = srchstr.substring(0,postext) + " && " + srchstr.substring(postext+1);
- postext += 4;
- nextIsItem = true;
- }
- }
- }
-
- if (i == 0) return "true";
- if (nextIsItem)
- {
- malformed = true;
- var lastAnd = srchstr.lastIndexOf('&&');
- var lastOr = srchstr.lastIndexOf('||');
- var lastOp = Math.max(lastAnd, lastOr);
- while (srchstr.lastIndexOf('(') > lastOp)
- {
- srchstr = srchstr.substring(0,srchstr.lastIndexOf('('));
- inParen--;
- }
- var lastParen = srchstr.lastIndexOf('(');
- srchstr = srchstr.substring(0,lastOp);
- }
-
- while (inParen > 0)
- {
- srchstr += ")";
- inParen--;
- }
-
- if (malformed)
- {
- const NF_SB = document.getElementById("newsfox-string-bundle");
- var msg = NF_SB.getString('malformed');
- msg += "\n\n" + NF_SB.getString('searchtext') + srchtext + "\n";
- srchstrG = srchstr.replace(/!/g,"-");
- srchstrG = srchstrG.replace(/ && /g, " ");
- srchstrG = srchstrG.replace(/ \|\| /g, " \| ");
- msg += NF_SB.getString('actualsearch') + srchstrG + "\n\n";
- }
- for (var j=0; j<i; j++)
- {
- repl = '"st[' + j + ']"';
- newrepl = "srchText(st[" + j + "], arttext)";
- srchstr = srchstr.replace(repl,newrepl);
- if (malformed) msg += "st[" + j + "]= " + st[j] + "\n";
- }
- if (malformed) alert(msg);
-
- return srchstr;
- }
-
- function setFeedbarButtons(type,title,hasHomepage)
- {
- var feedbarTitle = document.getElementById("feedTitle");
- feedbarTitle.value = title;
- var feedbarHome = document.getElementById("feedbarHome");
- feedbarHome.setAttribute("disabled",false);
- if (!hasHomepage) feedbarHome.setAttribute("disabled",true);
- var feedbarRefresh = document.getElementById("feedbarRefresh");
- feedbarRefresh.setAttribute("disabled",false);
- if (type == 2 || type == -1) feedbarRefresh.setAttribute("disabled",true);
- var feedbarMarkread = document.getElementById("feedbarMarkread");
- feedbarMarkread.setAttribute("disabled",false);
- if (type == -1) feedbarMarkread.setAttribute("disabled",true);
- var feedbarMarkunread = document.getElementById("feedbarMarkunread");
- feedbarMarkunread.setAttribute("disabled",false);
- if (type == -1) feedbarMarkunread.setAttribute("disabled",true);
- var feedbarDelete = document.getElementById("feedbarDelete");
- feedbarDelete.setAttribute("disabled",false);
- if (type == -1) feedbarDelete.setAttribute("disabled",true);
- var feedbarOptions = document.getElementById("feedbarOptions");
- feedbarOptions.setAttribute("disabled",false);
- if (type == 2 || type == -1) feedbarOptions.setAttribute("disabled",true);
- }
-
- ////////////////////////////////////////////////////////////
- ///// Sorting
- ///////////////////////////////////////////////////////////
-
- function artSort(by,dir,feed,items,itemFeed,itemIndex,type)
- {
- // sorter chooser
- var abc = function(by,dir)
- {
- if (dir != "ascending" && dir != "descending") return null;
- switch (by)
- {
- case "flag":
- if (dir == "descending") return FlagDown;
- else return FlagUp;
- case "title":
- if (dir == "descending") return TitleDown;
- else return TitleUp;
- case "read":
- if (dir == "descending") return ReadDown;
- else return ReadUp;
- case "date":
- if (dir == "descending") return DateDown;
- else return DateUp;
- }
- return null;
- }
-
- // sorters
- var FlagUp = function(a,b)
- {
- var tmp = ((gFmodel.get(itemFeed[b]).flags[itemIndex[b]] & 0x04) - (gFmodel.get(itemFeed[a]).flags[itemIndex[a]] & 0x04));
- if (tmp > 0) return 1;
- else if (tmp < 0) return -1;
- else return (a < b) ? -1 : 1;
- }
- // when gecko sort stable, can replace FlagUp body with the following line
- // { return ((gFmodel.get(itemFeed[b]).flags[itemIndex[b]] & 0x04) - (gFmodel.get(itemFeed[a]).flags[itemIndex[a]] & 0x04)); };
- var FlagDown = function(a,b)
- {
- var tmp = ((gFmodel.get(itemFeed[b]).flags[itemIndex[b]] & 0x04) - (gFmodel.get(itemFeed[a]).flags[itemIndex[a]] & 0x04));
- if (tmp > 0) return -1;
- else if (tmp < 0) return 1;
- else return (a < b) ? -1 : 1;
- }
- // when stable use next line
- // { return FlagUp(b,a); };
-
- var TitleUp = function(a,b) // don't worry about ties
- { return (items[a].title.toLowerCase() < items[b].title.toLowerCase() ? -1 : 1); };
- var TitleDown = function(a,b) { return TitleUp(b,a); };
-
- var ReadUp = function(a,b)
- {
- var tmp = ((gFmodel.get(itemFeed[b]).flags[itemIndex[b]] & 0x01) - (gFmodel.get(itemFeed[a]).flags[itemIndex[a]] & 0x01));
- if (tmp > 0) return 1;
- else if (tmp < 0) return -1;
- else return (a < b) ? -1 : 1;
- }
- // when stable use next line
- // { return ((gFmodel.get(itemFeed[b]).flags[itemIndex[b]] & 0x01) - (gFmodel.get(itemFeed[a]).flags[itemIndex[a]] & 0x01)); };
- var ReadDown = function(a,b)
- {
- var tmp = ((gFmodel.get(itemFeed[b]).flags[itemIndex[b]] & 0x01) - (gFmodel.get(itemFeed[a]).flags[itemIndex[a]] & 0x01));
- if (tmp > 0) return -1;
- else if (tmp < 0) return 1;
- else return (a < b) ? -1 : 1;
- }
- // when stable use next line
- // { return ReadUp(b,a); };
-
- var DateUp = function(a,b) // don't worry about ties
- { return (items[a].date < items[b].date ? -1 : 1); };
- var DateDown = function(a,b) { return DateUp(b,a); };
-
- // movers
- var tmpLoad = function(open,j,tmpA,tmpIF,tmpI,tmpF)
- {
- tmpA[open] = items[j];
- tmpIF[open] = itemFeed[j];
- tmpI[open] = itemIndex[j];
- tmpF[open] = feed.flags[itemIndex[j]]; // only used in feed sort,
- // so feed exists
- }
-
- var tmpUnload = function(j,open,tmpA,tmpIF,tmpI,tmpF)
- {
- items[j] = tmpA[open];
- if (type == 1) // feed
- {
- feed.set(j,items[j]);
- feed.flags[j] = tmpF[open];
- }
- else // group or category
- {
- itemFeed[j] = tmpIF[open];
- itemIndex[j] = tmpI[open];
- }
- }
-
- // the sort function
- var sorter = abc(by,dir);
- var N = items.length;
- var dummy = new Array(N);
- var invdum = new Array(N);
- var done = new Array(N);
- var tmpA = new Array();
- var tmpIF = new Array();
- var tmpI = new Array();
- var tmpF = new Array();
- for (var i=0; i<N; i++)
- {
- dummy[i] = i;
- done[i] = false;
- }
- dummy.sort(sorter);
- for (i=0; i<N; i++)
- invdum[dummy[i]] = i;
- var j, open;
- for (i=0; i<N; i++)
- if (!done[i])
- {
- j = invdum[i];
- tmpLoad(0,i,tmpA,tmpIF,tmpI,tmpF);
- open = 1;
- while (j != i)
- {
- tmpLoad(open,j,tmpA,tmpIF,tmpI,tmpF);
- open = 1 - open;
- tmpUnload(j,open,tmpA,tmpIF,tmpI,tmpF);
- done[dummy[j]] = true;
- j = invdum[j];
- }
- tmpUnload(j,1-open,tmpA,tmpIF,tmpI,tmpF);
- done[dummy[j]] = true;
- }
- }
-