<?xml version="1.0" encoding="UTF-8" ?>
<klip>
   	<owner>
      		<author>
         		Fr. 66
      		</author>
      		<copyright>
         		Copyright © 2008 Fr. 66 : Original Source © serence.com : Feed Content © Respective Suppliers
      		</copyright>
      		<email>
         		frater66@frater66.co.uk
      		</email>
      		<web>
         		http://www.frater66.co.uk/
      		</web>
   	</owner>
   	<identity>
      		<title>
         		Occult News Multi-Feed
      		</title>
      		<version>
         		1.0c
      		</version>
      		<uniqueid>
         		general_occult_news_multiFeed_klip
      		</uniqueid>
      		<lastmodified>
         		2008.04.04
      		</lastmodified>
      		<description>
         		Multifeed aggregator of general Occult and Esoteric news sites. Based upon KlipFolio's Multi-Feed Template. Updates : Removed Ursi's Eso Garden Blog.
      		</description>
      		<keywords>
         		multiple rss rdf feed viewer xml syndicate channel custom news reader occult esoteric gnostic magick magic
      		</keywords>
      		<template>
      			multifeed
      		</template>
   	</identity>
   	<locations>
      		<defaultlink>
         		http://www.frater66.co.uk/klip/klips.htm
      		</defaultlink>
      		<contentsource>
      			<!-- See multiFeeds[] below -->
      		</contentsource>
      		<icon>
         		http://www.frater66.co.uk/klip/icons/pentagram.ico
      		</icon>
      		<banner>
         		http://www.serence.com/support/samples/images/multiFeed_banner.gif
      		</banner>
      		<kliplocation>
         		<!-- 
         		Be sure to put your own URL here if you publish this Klip to users so you
              		can control the updates.
           		-->
         		http://www.frater66.co.uk/klip/general_occult_multiFeed.klip
      		</kliplocation>
   	</locations>
   	<setup>
      		<refresh>
         		60
      		</refresh>
      		<country>
         		--
      		</country>
      		<language>
         		--
      		</language>
      		<report>
      			true
      		</report>
   	</setup>
   	<messages>
      		<loading>
         		Loading data ...
      		</loading>
      		<nodata>
         		No headlines to display.
      		</nodata>
   	</messages>
	<style>
		<!-- 
		The style information is used to parse the RSS feeds.
		-->
		channel > title, feed title	{ type: scratch; name: "Channel Title"; }
		channel image url		{ type: scratch; name: "Channel Image"; }
		item, entry						
			{ type: item; definition: "icon,visibletitle,matchtitle,source,link,storyimage,description,sourcerow,banner,pubdate,guid2"; }


		storyimage 
		{
			type:		image;
			noterow:	3;
			notelabel:	false;
			key:		exclude;
			name:		"storyimage";
		}

		description, note, content:encoded, content 
		{
			type:		text;
			noterow:	4;
			notelabel:	false;
			content:	cdata;
			order:		1;
			key:		exclude;
			name:		"description";
		}

		summary 
		{
			type:		text;
			noterow:	4;
			notelabel:	false;
			content:	cdata;
			order:		2;
			key:		exclude;
			name:		"description";
		}

		link:not([rel="service.edit"]):not([href]),
		link:not([rel="service.edit"])::attribute(href)
		{
			type:		link;
			key:		exclude;
			name:		"link";
			order:		1;
		}

		guid:not([isPermaLink="false"])
		{
			type:		link;
			key:		exclude;
			name:		"link";
			order:		2;
		}

		guid 
		{
			type:		text;
			key:		include;
			name:		"guid2";
			order:		1;
		}


		pubDate, dc:date, modified 
		{
			type:		pubdate;
			key:		exclude;
			name:		"pubdate";
		}

		banner 
		{
			type:		image;
			noterow:	10;
			notelabel:	false;
			key:		exclude;
		}

		icon 
		{
			type:		image;
			itemcol:	1;
			key:		exclude;
		}

		sourcerow 
		{
			type:		text;
			noterow:	9;
			wrap:		false;
			key:		exclude;
			label:		"Source";
		}

		source 
		{
			type:		text;
			itemcol:	3;
			wrap:		false;
			key:		exclude;
		}

		title 
		{
			type:		text;
			key:		include;
			name:		"matchtitle";
		}

		visibletitle 
		{
			itemcol:	1;
			noterow:	1;
			wrap:		false;
			notelabel:	false;
			emphasis:	strong;
			key:		exclude;
		}

	</style>
   
<klipscript>
	<![CDATA[
//
//  The following multiFeeds[] array lets you specify multiple Feeds.   The format is
//
//   Initially selected  Feed URL                             Name            Icon URL
//   ------------------  -----------------------------------  --------------  -----------------------------------------------------------------------
//   [0]                 [1]                                  [2]	      [3]
//   false,              "http://rss.cbc.ca/sportsnews.xml",  "Sports News",  "http://www.serence.com/serence_klips/pics/feedviewer_3/bullets/37.png"
//
//   Using "-" as the first character for a URL causes the Klip to make a new Setup tab 
//   using the Name string for the name of the tab.  The text after the "-" character can 
//   be anything, such as a tab label or a note to yourself.  Note that the tabs are 
//   added to the Setup window in reverse order - the last tab you specify in the array 
//   will become the left-most tab.
//   
//   The icon URL is ignored for entries which specify tabs.  If you don't want icons
//   beside the item headlines, use "" (an empty string) as the Icon URL entry for all
//   the entries in multiFeeds.  The example icons below are from a set of icons on 
//   the Serence website, with icons named from "0.png" to "69.png", but any URL to a 
//   valid image can be used instead.
//
//  The sample feeds in this template Klip are from http://www.cbc.ca/rss/
//

var multiFeeds = [


   	[ false, "- Tab 3", "Blogs", "" ],



   	[ false, "http://www.occult-underground.com/blog/feed", "Occult Underground Blog", "http://www.frater66.co.uk/klip/icons/OccUg.ico" ],

   	[ false, "http://feeds.feedburner.com/plutonica", "Plutonica.net", "http://www.frater66.co.uk/klip/icons/plutonica.ico" ],

   	[ false, "http://risingphoenixfoundation.wordpress.com/feed", "The Rising Phoenix Foundation", "http://www.frater66.co.uk/klip/icons/wp.ico" ],





   	[ false, "- Tab 2", "Gnostic", "" ],


   	[ false, "http://emeraldcitygnosis.blogspot.com/feeds/posts/default", "Emerald City Gnosis", "http://www.frater66.co.uk/klip/icons/bs.ico" ],

   	[ false, "http://feeds.feedburner.com/GnosticPath", "Gnostic Path", "http://www.frater66.co.uk/klip/icons/bs.ico" ],



   	[ false, "- Tab 1", "General", "" ],


   	[ false, "http://abrahadabra.net/feed/", "abrahadabra.net", "http://www.frater66.co.uk/klip/bullets/59.png" ],

   	[ false, "http://www.greylodge.org/RSS/rss.xml", "Grey Lodge Occult Review", "http://www.frater66.co.uk/klip/icons/greylodge.ico" ],

   	[ false, "http://www.key64.net/feeds/rss", "Key 62", "http://www.frater66.co.uk/klip/bullets/63.png" ],

   	[ false, "http://www.occultofpersonality.com/feed/", "Occult of Personality", "http://www.frater66.co.uk/klip/icons/occultofpersonality.png" ],


];

// Indexes for the multiFeeds array
var INITIAL_STATE = 0;
var URL = 1;
var NAME = 2;
var ICON = 3;

// Array of KlipFolio List Control objects
// See http://www.serence.com/developer/api/ListControl.html
//
var lcSource = new Array();

// Global variable to let onRefresh() optimize its refresh when
// the user has clicked on an item in the ListControl
var userClickedListControl = false;      // User clicked on a name in the checkbox

var g_current_feed;


function onLoad()
{ 
   	_t( "onLoad()" );
   
   	var lc_index = -1;
   	var tab;
   
   	//
   	// Loop through all the feeds and build the Klip Setup tab
   	//
	for (var i = 0; i < multiFeeds.length; i++)
   	{
   	   	if ( multiFeeds[i][URL].indexOf("-") == 0 )
   	   	{
   	   	   	//
   	   	   	// Got a "-" character so create a new tab for subsequent URLs
   	   	   	//
   	   	   	lc_index++;
   	   	   	tab = Setup.addTab( multiFeeds[i][NAME] );
			
   	   	   	tab.addText( "Choose your sources to monitor:" );
   	   	   	lcSource[ lc_index ] = tab.addListControl( true, true );
   	   	   	lcSource[ lc_index ].onStateChange = lcSource_onStateChange;
   	   	   	
   	   	   	//
   	   	   	// Every object in KlipFolio can have a custom property just by declaring it.  
   	   	   	// Here we store an index related to the entry in the array multiFeeds[]
   	   	   	//
   	   	   	lcSource[ lc_index ].offset = i + 1;
   	   	   	lcSource[ lc_index ].name = multiFeeds[i][NAME];
   	   	} 
   		else 
   	   	{
   	      		//
   	      		// Add this URL to the current ListControl
   	      		//
   	      		var initial_state = multiFeeds[i][INITIAL_STATE];		// true or false
   	      		var name          = multiFeeds[i][NAME];
   	      		var url           = multiFeeds[i][URL];
   	      		
   	      		var checked;
			var prefs_name = lcSource[ lc_index ].name + "|" + name;
					 
   	      		if( Prefs.getPref( prefs_name ) == "" )
   	      		{
   	      		   	// No initial state
   	      		   	state = initial_state;
   	      		   	Prefs.setPref( prefs_name, checked );
   	      		} 
   	      		else 
   	      		{
   	      		   	// get saved checked/unchecked state
   	      		   	state = Prefs.getPref( prefs_name ) == "true" ? true : false;
   	      		}
   	   		
   	      		lcSource[ lc_index ].addItem( name, state );
   	      		multiFeeds[i].updated = state;
   	  	}	
   	}
   
   	for (var i = 0; i < Items.length; ++i)
   	{
   	   Items[i].tagged = true;		// These items have already been processed
   	}
	
   	UpdatePrefs();
   	
   	// Set up callbacks to handle item creation
   	Engines.KlipFood.onCreate = myCreate;
	Engines.KlipFood.onUpdate = myUpdate;
   
   	if( gTrace )    // Add a debug tab in the Klip
   	{            
   	   	button = tab.addButton( "Show State in Trace Window" );
   	   	button.onClick = show_state;
   	}
}


//
// Called when a ListControl was clicked.  
//
// Because we are refreshing the Klip in between user events, it's possible for the 
// user to quickly click multiple items in the list control before we get an event
// from KlipFolio that the list control was updated.
//
// So, we keep a shadow value for each list control item called .previous_state
//
// We use the property .offset to find the index of this entry 
//
function lcSource_onStateChange( index )
{
   	_t( "Item: "   + this[index] );   
   	_t( "Offset: " + this.offset );   
   	_t( "State: "  + this.getState(index) );   
   	
   	var name   = multiFeeds[index + this.offset][NAME];
   	var state  = this.getState(index);
   	
   	_t( "You clicked on: " + name );  
   	
   	UpdatePrefs();
   	// update the state of all visible items in the list.  Use the Item's name, 
   	// which is stored in .extra, as a hash lookup for Prefs.getPref()
   	for (var i = 0; i < Items.length; ++i)
   	{
   	   	Items[i].hidden = Prefs.getPref( Items[i].extra ) == "true" ? false : true;
   	}
   	
   	//
   	// Request a refresh to update any feeds the user has changed to 
   	// now show.
   	//
   	// Comment out these two lines to have the user click the Refresh button in the Klip setup
   	// 
   	userClickedListControl = true;
   	Klip.requestRefresh();
} 

function UpdatePrefs()
{
   	//
   	// Store the state of the prefs for all listControls.
   	//
   	for (var lc_index = 0; lc_index < lcSource.length; lc_index++) 
   	{
		for (var i = 0; i < lcSource[lc_index].length; i++ ) 
		{
			var prefs_name = lcSource[ lc_index ].name + "|" + lcSource[lc_index][i];
			Prefs.setPref( prefs_name, lcSource[lc_index].getState( i ) );
		}
   	}
}

//
// Mark all items in Items[] that have been marked as coming from 
// the feed of the given name
//
function items_mark( name ) 
{
   	for (var i=0; i<Items.length; i++) 
   	{
	      	if( Items[i].extra == name ) 
	      	{
	      	   	Items[i].checklastmodified = Items[i].lastmodified;
	      	   	Items[i].hidden = false;
	      	} 
	      	else 
	      	{
	      	   	Items[i].checklastmodified = null;
	      	}
   	}
}

//
// Remove all the items in the list associated with name that have *not*
// been updated.
//
// The test is to see if the .lastmodified date is still the same as the
// custom property .checklastmodified
//
function items_purge( name ) 
{
	var i = 0;
	var length = Items.length;

	while(i < length)
	{
      		if( Items[i].extra == name ) 
      		{
	      		// If this item has a .checklastmodified, see if it has been updated; if
	         	// not, purge it.
	          	if (Items[i].checklastmodified &&
	                    Items[i].checklastmodified == Items[i].lastmodified ) 
	                {
		             	// remove the item permanently since we want to show all items
	 	            	// in a feed if a user checks it again
		             	Items.remove( i, true );
				length--;
				continue;
	         	}
      		}
      		i++;
	}
}

function onRefresh()
{
	_t( "onRefresh()" );
	
   	var result = true;
   
   	if ( userClickedListControl )
   	{
   	   	_t( "onRefresh() triggered from List Control" );
   	}
   	
   	// Mark all the items
   	for (var i = 0; i < Items.length; i++)
   	{
   	   	Items[i].tagged = true;
   	}
	
   	for (var lc_index = 0; lc_index < lcSource.length; lc_index++) 
   	{
   	   	for (var i = 0; i < lcSource[lc_index].length; i++ ) 
   	   	{
   	   	  	var index = lcSource[lc_index].offset + i;
   	   	  	var name = lcSource[lc_index].name + "|" + lcSource[lc_index][i]
   	   	  	var url = multiFeeds[index][URL];
   	   	  
   	   	  	if (lcSource[lc_index].getState( i ) )
   	   	  	{
   	   	  	  	// An optimization -- if onRefresh() has been requested by the
   	   	  	  	// event handler in the ListControl, then only update a feed
   	   	  	  	// if it was previously unchecked
   	   	  	  	//
   	   	  	  	//  userClickedListControl    Action
   	   	  	  	//  ----------------------    ---------------------------------
   	   	  	  	//  false                     Update every feed
   	   	  	  	//
   	   	  	  	//  true                      Update feed only if checked is false
   	   	  	  	//          
   	   	  	  	if ( !userClickedListControl ||
   	   	  	  	     multiFeeds[index].updated == false ) 
   	   	  	  	{
   	   	  	  		g_current_feed = index;
					// Need to refresh because user has changed it to true 	    													
	   	   	  	    	result = getData( url, name ) && result;
   	   	  	                      	  
	   	   	  	        multiFeeds[index].updated = true;
	   	   	  	} 
   	   	  	} 
   	   	  	else 
   	   	  	{
				multiFeeds[index].updated = false;
   	   	  	}
   	   	}
   	}
   
   	// Clear global state
   	userClickedListControl = false;
   	
   	// Clear global state
   	return result;
}


//
// Refresh only the items for the given URL. 
//
function getData( url, name )
{
   	_t( "Refreshing  : " + url );
   
   	req = Engines.HTTP.newRequest (url);

   	if ( !req.send() ) 
   	{
   	   	_t( "Unable to send url" );
   	   	return false;
   	}
	
   	var data = req.response.data;
   	if( !data.length ) 
   	{
   	  	// Check if we got a "304 Not Modified message" in the headers
   	  	//
   	  	// true -- data has not changed since we last checked it
   	  	// false -- no data for another reason, return false
	
   	  	var re_304 = new RegExp( "304 Not Modified" );
   	  	var check = re_304.exec( req.response.headers ) != null;
   	  	if ( check ) 
   	  	{
   	  		_t( "304 NOT MODIFIED returned -- no refresh needed" );
   	  	}
   	  	return( check );
  	}	
	
   	// At this point we have data, so let's refresh this source and purge
   	// any items that are not updated
   	//   
   	items_mark( name );     // Mark all items for this source
   	Engines.KlipFood.process (req.response);
   	items_purge( name );    // Delete all items not updated
   	
   	_t( "              done" );
	
   	// Update the show of sources
   	//
   	
   	var count = 0;
   	for (var i = 0; i < Items.length; i++)
   	{
   	   	if (!Items[i].tagged)
   	   	{
   	   	   	Items[i].extra  = name;
   	   	   	Items[i].tagged = true;
   	   	   	
   	   	   	count++;
   	   	}
   	}
   
   	_t(" Updated #: " + count );
   
   	return true;
}

//
// The myCreate function modifies the properties of each new item that is created.
//
function myCreate (new_item)
{
	_t( "myCreate()" );
	if (multiFeeds.length)
	{
		var source = Klip.processEntities (Engines.KlipFood.getScratch ("Channel Title"));
		new_item.setData ("sourcerow", source);
		if (multiFeeds.length > 1)
		{
			new_item.setData ("source", source);
			new_item.setData ("icon", multiFeeds[g_current_feed][ICON]);
		}

		new_item.setData ("banner", Klip.processEntities (Engines.KlipFood.getScratch ("Channel Image")));

		// note that the replace on <strong> tags is to fix feedster's naive highlighting of search terms
		var data = Klip.processEntities (new_item.getData ("description")).replace (/(<strong>|<\/strong>)/g, "");
		
		var index;
		var m;

		//
		// Retrieve an image from the news item and display it in the note
		//
		var imgdata = data;
		var done = false;
		while (!done && ((index = imgdata.indexOf ("<img")) != -1))
		{
			imgdata = imgdata.substring (index + 4, imgdata.length);
			m = imgdata;
			var img = "";
			var index2;
			if ((index2 = m.indexOf ("src=")) != -1)
			{
				m = m.substring (index2 + 4, m.length);
				var ch = m[0];
				if (ch == '"' || ch == "'")
				{
					index2 = m.indexOf (ch, 1);
					if (index != -1)
					{
						img = m.substring (1, index2);
					}
				}
				else
				{
					if	((index2 = m.indexOf (' ')) != -1) {}
					else if ((index2 = m.indexOf ('>')) != -1) {}
					if (index2 != -1)
					{
						img = m.substring (0, index2);
					}
				}
			}
			img = Klip.convertToText (img);
			if (img.length)
			{
				if (!imageBlacklisted (img))
				{
					new_item.setData ("storyimage", img);
					done = true;
				}
			}
		} //while
		
		data = Klip.convertToText (data);
		new_item.setData ("description", data);

		//
		// Fix for livejournal entries without titles
		//
		var test_data = new_item.getData ("matchtitle");
		if (test_data.length)
		{
			new_item.setData ("visibletitle", test_data)
		}
		else
		{
			var first_line_re = new RegExp (".........*?[.:?\r\n]");
			if (m = first_line_re.exec (data))
			{
				data = m[0];
				if (data[data.length-1] == '\r' || data[data.length-1] == '\n')
				{
					data = data.substring (0, data.length);
				}
			}
			data = data.substring (0, 80);
			if (data.length == 80)
			{
				if ((index = data.lastIndexOf (' ')) > 25)
				{
					data = data.substring (0, index);
				}
				data += "...";
			}
			new_item.setData ("visibletitle", data)
		}
	}
	return true;
}

function myUpdate (existing_item, properties)
{
	// Update using the new properties
	_t( "myUpdate()" );
	myCreate(properties);
	return true;
}

function imageBlacklisted (url)
{
	if (url.indexOf ("r.feedster.net") != -1)
	{
		return true;
	}
	return false;
}


// ---------------- Useful Trace Code -------------------

function show_state() 
{
   	_t( "show_state()" );
   	dumpItems();
   	dumpItemsDeleted();
   	show_all_prefs();
   	queryListControl();
}

function dumpItems() 
{
    	if( Items.length == 0 ) 
    	{
    	   	trace( "There are no items in the Items[] array\r\n" );
    	   	return;
    	}

    	for( i = 0; i < Items.length; i++ ) 
    	{
   		trace( "Items[" + i + "]: \r\n" );       
   		queryItem( Items[i] );
    	}
}

function dumpItemsDeleted() 
{
    	if( Items.Deleted.length == 0 ) 
    	{
       		trace( "There are no items in the Items.Deleted[] array\r\n" );
       		return;
    	}
   
    	for( i = 0; i < Items.Deleted.length; i++ ) 
    	{
      		trace( "Items.Deleted[" + i + "]: \r\n" );       
      		queryItem( Items.Deleted[i] );
    	}
}

function queryItem( item ) 
{
    	trace( "----\r\n" );
    	trace( "    item.text         = \"" + item.text + "\"  (string)\r\n" );
    	trace( "    item.url          = \"" + item.url + "\"  (string)\r\n" );
    	trace( "    item.note         = \"" + item.note + "\"  (string)\r\n" );
    	trace( "    item.extra        = \"" + item.extra + "\"  (string)\r\n" );
    	trace( "    item.hidden       = " + item.hidden + " (boolean)\r\n" );
    	trace( "    item.visited      = " + item.visited + " (boolean)\r\n" );
    	trace( "    item.canpurge     = " + item.canpurge + "  (boolean)\r\n" );
    	trace( "    item.alerting     = " + item.alerting + " (boolean)\r\n" );
    	trace( "    item.creation     = " + item.creation + " (double)\r\n" );
    	trace( "    item.pubdate      = " + item.pubdate + "  (double)\r\n" );
    	trace( "    item.lastmodified = " + item.lastmodified + "  (double)\r\n" );
    	trace(" -- \r\n" );
    	trace( "    item.tagged = " + item.tagged + "  (text)\r\n" );
   	
    	trace( "\r\n" );
}

function queryListControl() 
{
   	for (var lc_index = 0; lc_index < lcSource.length; lc_index++) 
   	{
   	   	var index = lcSource[lc_index].offset + i;
   	   	for (var i = 0; i < lcSource[lc_index].length; i++ )
   	   	{
		    _t( "lcSource: " + lcSource[lc_index].getState( i ) + "  multiFeeds: " + 
		    multiFeeds[i + lcSource[lc_index].offset].updated + " - name: " + 
		    multiFeeds[i + lcSource[lc_index].offset][NAME] );
		}
	}
}

function show_all_prefs() 
{
   	var i;
   	trace( "------------------------\r\n" );
   
   	for (i = 0; i < Prefs.length; i++) 
   	{
   	   	trace("Prefs["+i+"]: '"+Prefs[i].name+"' == '"+Prefs[i].value+"'\r\n");
   	}
   
   	trace( "------------------------\r\n" );
}

//
// Trace code
//
var gTrace = false;            // Turn on trace code
function _t( s ) 
{
   	if ( gTrace ) 
   	{
   	   	trace( Prefs.title + ": " + s + "\r\n" );
   	}
}

	]]>
</klipscript>
</klip>