Differences from version 7 to 11



@@ -1,13 +1,13 @@

 {maketoc}
 ! Introduction
-This Tutorial concerns Liberty Data Plugins and trys to answer the Basic Questions - What / Where / How & Why. It uses simple English most of the time but does get Technical in place (the contents of source code is displayed). It describes what each sections within a Liberty Data Plugin does and how the sections interact. What this Tutorial __Does Not Do__ - is Provide a __))Step-by-Step((__ Methodology for Creating a Liberty Plugin. The __((Tutorial - Liberty Plugins II))__ does that and a __Lot__ more.
+This Tutorial concerns Liberty Data Plugins and tries to answer the Basic Questions - What / Where / How & Why. It uses simple English most of the time but does get Technical in place (the contents of source code is displayed). It describes what each sections within a Liberty Data Plugin does and how the sections interact. What this Tutorial __Does Not Do__ - is Provide a __))Step-by-Step((__ Methodology for Creating a Liberty Plugin. The __((Tutorial - Liberty Plugins II))__ does that and a __Lot__ more.
 
 ! Should I be Reading This?
-This Tutorial is provided primarilly for Site Administrators and Developers (or anyone) wanting to add functionallity to a bitweaver Powered Site. A User on a bitweaver Powered Site would gain a little knowledge from reading the Heading __"How is a Plugin Used?"__.
+This Tutorial is provided primarily for Site Administrators and Developers (or anyone) wanting to add functionality to a bitweaver Powered Site. A User on a bitweaver Powered Site would gain a little knowledge from reading the Heading __"How is a Plugin Used?"__.
 
 ! So what is a Liberty Plugin?
-! And why do you keep saying Liberty anyway - isn't Plugin enough?
-There are all kinds of Plugins. Every Package incorporated into __bitweaver__ (in Application Framework mode) has it's own definition of what a Plugin is / what it does / and where it is used. Even within the ((LibertyPackage)) there are even three different types of Plugins. They are:
+! And why do you keep saying Liberty Data Plugins anyway - isn't Plugin enough?
+There are all kinds of Plugins. Every Package incorporated into __bitweaver__ (in it's Application Framework Mode) has it's own definition of what a Plugin is / what it does / and where it is used. Even within the ((LibertyPackage)) there are even three different kinds of Plugins. They are:
 * Liberty Data Plugins - What this tutorial is all about.
 * Liberty Format Plugins - Used to handle different types of data.
 * Liberty Storage Plugins - Used to store the data in different ways.

@@ -16,32 +16,34 @@

 
 If it will make things easier though - in the remainder of this Tutorial - all __Plugins__ are __Liberty Data Plugins__.
 
-! How can a Plugin Add Functionallity?
-Easy! Here is the situation. You are updating a page - and when your finished you look at it and say "Not Bad - but I wish I had some ))EyeCandy(( to make this more noticable". If you were doing things the old fashioned way using pure HTML pages - there are thousands of different things that could be easilly added to give you exactly the effect you want - if you have the skill to add it. Wiki page are not like a pure HTML pages - but anything that works in an HTML can be placed in a Plugin and will work in a Wiki page.
-The Plugin __"))EyeCandy(( ))NeonText((" (ECNT)__ Plugin is a very good example. It was written for two reasons:
+! How can a Plugin Add Functionality?
+Easy! Here's a situation for you. You've just updated a page - and say "Not Bad - I just wish I had some ))EyeCandy(( to make this more noticeable". Now if we were doing things the old fashioned way using pure HTML pages - there are literally thousands of different things that could be easily added to give you exactly the effect you want - providing you have the skill to add it. Bitweaver's pages are not like a pure HTML pages - but anything that works in an HTML page can be placed in a Plugin and will work in a bitweaver page.
+The __"))EyeCandy(( ))NeonText((" (ECNT)__ Plugin is a very good example. It was written for two reasons:
 # I renamed a finished a Proposed Plugin ((PluginProposal-HideText)) and wanted some ))EyeCandy(( to make a __Notice - It's Done__ statement.
 # I decided to create the second Tutorial ((Tutorial - Liberty Plugins II)) and needed a good example for it.
-__Note:__ The Plugin __"))EyeCandy(( ))NeonText((" (ECNT)__ applies a Neon Lighting Effect (an age old technique of attracting attention) to a string. A DHTML Script file from [http://www.dynamicdrive.com] was used as the source.
-Of course - not everything is as simple as this - but sometimes the simple things are the most rewarding.
+__Note:__ The Plugin __"))EyeCandy(( ))NeonText((" (ECNT)__ applies a Neon Lighting Effect (an age old technique of attracting attention) to a string. The original source is a DHTML Script file from [http://www.dynamicdrive.com]. Of course - not everything is as simple as this was - but sometimes the simple things are the most rewarding.
 
 ! But I'm not a Computer Guru!
-You don't have to be. At the minimum - all that is required is a little knowledge of HTML / the PHP language / and the ability to look things up and the ability to experiment a little. Plugins can be fairly simple or as complex as you want to them to be. They provide an excelent place for a beginner to play and learn.
+You don't have to be. At the minimum - all that is required is a little knowledge of HTML / the PHP language / the ability to look things up / and the ability to experiment a little. Plugins can be as simple or as complex as you want to them to be. They provide an excellent place for a beginner to play and learn.
 
 ^::__The Plugin "Comment" will be used as an example throughout this Tutorial.__::^
 
 ! What does this Plugin do?
 The Comment Plugin is the simplest Plugin imaginable. All it does is - well - nothing. By doing "nothing" though - it allow text to be stored in a page that is not displayed or processed in any way.
-! So how is a Plugin used?
-The Comment Plugin (unlike most Plugins) operates on the text encased between a pair of Code Blocks. A Code Block is the name of the Plugin within a pair of curly brackets “{“ and “}” like this: ~123~))COMMENT(( ~125~
-~123~))COMMENT(( ~125~ The Text Inside the Block ~123~COMMENT~125~
-In this case - the Comment Plugin prevents the text from being displayed.
+! How is a Plugin Used?
+The __Comment__ Plugin allows text to be stored in a page - and prevents it from being displayed. It is used like this: ~123~))COMMENT(( ~125~ The Text Inside the Block ~123~COMMENT~125~
 
-To make Plugins more functional, additional information (parameters) can be passed to it. The parameters are added to the first code block in the form of parameter_name=’value’. Each Plugin defines the names of the parameters and what is expected. Most Plugins accept multiple parameters – which are separated by a space character.
-__Please Note:__ The 'Value' does not always have to be encased in quotes. A single number (123456) / single words like TRUE / FALSE / or ))KeyWords(( (defined by the plugin) do not require quotation. __Strings Do__! Any text that contains whitespaces or non-alphanumeric characters is considered to be a String and __Has__ to be quoted. The quote character can be either a single quote or a double quote (characters ' or ").
-__Note 2:__ Capitolization is not manditory. ~123~))COMMENT(( ~125~ = ~123~comment ~125~ / and / ))PARAMETER_NAME(( = ))Prameter_Name(( = ))parameter_name((.
-! Where do I find these Plugins?
-The best way to become fluent with the Plugins is to use them. A complete listing of all Active Plugins can be found at the bottom of the page while editing a Page. Look for the __Plugin Help__ tab. Selecting “More Details” shows the Plugins Parameters are and what they do. It will also show an example or two.
-The Files are stored in the Liberty/Plugins directory. The files use a strict file naming convention that must be adhered to. For our Example Plugin, the filename is “data.example.php”.
+The Comment Plugin (unlike most Plugins) operates on the text encased between a pair of __Code Blocks__. A __Code Block__ is the __Key__ used to start the Plugin encased in curly brackets “{“ and “}” like this: ~123~))COMMENT(( ~125~
+__Parameters__ are used to make a Plugins more useful. They provide additional information / and control how the Plugin works. Parameters are added to the first code block in the form of __parameter_name=’value’__. This works because the Plugin defines each __Parameter__ that it will use and knows what to expect. The __Value__ can be a single number (123456) / single words like TRUE / FALSE / or a ))KeyWord(( (again defined by the Plugin). None of these __Has__ to be quoted. It can also be a __String__ - defined as:
+Any series of numbers or text that contains white spaces or non-alphanumeric characters.
+__Must be Quoted__ - the quote character can be either a single quote or a double quote (characters ' or ").
+
+__Note:__ Capitalization is __Not__ mandatory with the Plugins Key or with Parameter Names.
+__~123~))COMMENT(( ~125~ = ~123~Comment ~125~ = ~123~comment ~125~ / and / ))PARAMETER_NAME(( = ))Prameter_Name(( = ))parameter_name((__
+With the Parameter's __Value__ - it could be!
+! Where do I find the Plugins?
+The best way to become fluent with the Plugins is to use them. A complete listing of all Active Plugins can be found inside the __Page Editor__ at the bottom of the page. Look for the __Plugin Help__ tab. Selecting __“More Details”__ shows each of the Plugin's Parameters and explains what they do. Normally - an __Example__ or two is also given.
+The Files are stored in the Liberty/Plugins directory. The files use a strict file naming convention that must be adhered to. For our Example Plugin, that filename is “data.example.php”.
 
 ! Show me a Plugin then.
 {CODE source=php num=on }<?php

@@ -101,15 +103,24 @@

 function data_comment($data, $params) {
  return ' ';
 }
-?>
-That's all there is to it. You should be able to see why I said the Plugin does nothing. The Load Function returns a single space character. You should also be able to see the 4 basic sections in the Plugin. Each of these will be explained below. They are:__
-# Copyright
-# Initialization
-# Help Function
-# Load Function__
+?>{CODE}
+That's all there is to it. You should be able to see the 4 basic sections in every Plugin. Each of these will be explained below. They are:
+# __Copyright__
+# __Initialization__
+# __Help Function__
+# __Load Function__
+
+! The Way It Works!
+When a Web Browser asks bitweaver to load a page - bitweaver accesses the database for that page and looks at it to see what has to be done. It scans each and every line looking for Special Symbols / ))Key-Words(( and HTML Statements that it has to act uppon. One of the things it looks for are __Code Blocks__ like this:
+~123~))COMMENT(( parameter='No' ~125~ The Text Inside the Blocks ~123~COMMENT~125~
+When it finds one - it looks up the __Plugins Tag__ and passes the data to the __Load Function__ (in this case the Load Function is __function data_comment($data, $params)__). The two variables passed to it are:
+*__$data__ - contains all of the information between the two __Code Blocks__ - (in this case that would be: __The Text Inside the Blocks__)
+*__$params__ is an array (in this case that array contains a single element with the __index__ of __'parameter_name'__ and with a __value__ of __'No'__). Now if this seems to be getting difficult - relax. There was no reason to include the code in the Comment Plugin - but most Plugins extract the parameter array into variables like this: __extract ($params);__ which would create all of the variables in the array (in this case - __$parameter_name__ and it would have a value of __No__)
+When the Plugin is finished doing whatever it is designed to do - (in this case - nothing) - it finishes and returns a value to bitweaver for processing - (in this case - that was done with __return ' ';__). __Bitweaver adds that information to the page it is building and continues stepping through the data. When this is done the page is displayed.
+
 !! Copyright
-Hay - with a name like that - what else did you expect?
-{CODE source=php num=on }{CODE source=php num=on }<?php
+With a heading like that - what would you expect to find here?
+{CODE source=php num=on }<?php
 // $Id: data.comment.php,v 1.1.2.3 2005/07/16 03:52:25 starrrider Exp $
 /**
  * assigned_modules

@@ -122,13 +133,11 @@

  * All Rights Reserved. See copyright.txt for details and a complete list of authors.
  * @license Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
  */{CODE}
- The Copyright Section does do a little more than just provide Copyright data. It is an integral part of our Documentation Effort. We are using the Doxygen Documentation System to help us build our API Documentation. For more information - please read ((APIDocumentation))
-{DD title='Line 1' }__<?php__ - tells the Browser that the code is php{DD}
-{DD title='Line 2' }Line 2 will be changed by the CVS Revision System each time the file is Commited. In this case - starrrider made the Commit at 03:52 (3:52 AM) on July 7, 2005 - it also gives the name of the file. None of this needs to be changed when creating a new Plugin.{DD}
-{DD title='Overview' }Most of this section contains variables that are supplied to Doxygen System and never need to be edited. The only one that needs to be edited when creating a __New Plugin__ is the __author__. In this case that is __"Me"__ and I always include my ))SourceForge(( email address. All the rest of the variables are never changing or will be change automatically ( __$Id:__ and __version__).{DD}
+This Section does more than just post Copyright data though. It is an integral part of our Documentation Effort. We are using the Doxygen Documentation System to help us build our API Documentation. For more information - please read ((APIDocumentation))
+{DD title='Explaining Line 1' }The only line in this section that isn't a comment. The first line in every __php File__ tells the Browser what kind of code it is dealing with - __php__{DD} {DD title='Explaining Line 2' }Line 2 will be changed by the CVS Revision System each time the file is Committed. In this case - the last Commit was made by __"Me"__ at 03:52 (3:52 AM) on July 7, 2005.{DD} {DD title='Providing an Overview' }Most of this section contains variables that are supplied to Doxygen System and never need to be edited. The only one that needs to be edited when creating a __New Plugin__ is the __author__. In this case - that is __"Me"__. It is not mandatory but I include my email address in these Plugins. I use my ))SourceForge(( email address mainly because it is a redirect and will remain stable if I happen to move or change ISP's. All the rest of the variables are never changing or will be change automatically ( __$Id:__ and __version__).{DD}
 !! Initialization
 This is where Globals and Defines are kept. It is also where the GUID is defined and then Registered with the Liberty System
-{CODE source=php num=on }/******************
+{CODE source=php num=14 }/******************
  * Initialization *
  ******************/
 global $gLibertySystem;

@@ -147,25 +156,21 @@

 );
 $gLibertySystem->registerPlugin( PLUGIN_GUID_COMMENT, $pluginParams );
 $gLibertySystem->registerDataTag( $pluginParams['tag'], PLUGIN_GUID_COMMENT );{CODE}
-This entire section is __Mandantory__ and only the values should be changed.
-
-{DD title='The Comments'}When creating a new Plugin - each of the Comments (both upper & lower case) should be changed to the name of your new Plugin. The Capitolization should remain as it is. Capitolized "Comments" (Lines 5, 18, & 19) relate to the GUID / While Lines 7 & 15 are described below.{DD}
-{DD title='tag'}The value given here dontrols what the user will have to enter to make your function operate. Most of the __tag__ should be the same as the name given to the Plugin. When the name is unweldy because of it's length - we can shorten it to whatever is desired. The plugin ))DropDown(( as an example was shortened to 'DD' because I expected it to be used a lot - it creates the expandable box you looking at.{DD}
-{DD title='auto_activate'}Plugins can be turned off in bitweaver. A default value can be provided by specifing __TRUE__ or __FALSE__ here. This gives you the ability to turn a plugin off when you know there are problems with it.{DD}
-{DD title='requires_pair'}This controls how the Plugin operates. When __TRUE__ all of the information needed by the Plugin is contained in a single Code Block. Plugins like __))AddTabs((__ and __))AgentInfo((__ operate with this model. When it is __FALSE__ - 2 Code Blocks are needed (the first to start the Plugin running and the second to end it). Normally - this type of Plugin operates on the Data located between the Code Blocks. Both __Comment__ and __))DropDown__ operate with this model.{DD}
-{DD title='load_function'}This specifies the name of the function that does all the work and is called by bitweaver. I normally use a name like 'data_X' where X is the name of the plugin. __Note__ Be sure to actually give the __Load Function__ the same name. Please don't laugh - I've made that mistake. In some editors it's hard to see if there are 1 or 2 underline character.{DD}
-{DD title='title'}This is an "Unofficial" name for the Plugin that is only used by Help routine. If the name of your Plugin is __"))AardvarkAreCute(("__ and you specified the __Tag__ to __))AAC((__ then the __Ttitle__ should probably be __"Aardvark Are Cute (AAC)"__. The latter provides a visual clue to the users shouldn't be able to miss.{DD}
-{DD title='help_page'}Every Plugin should have a Help Page on bitweaver. The value given ends up as a URL - so while viewing the limited Help provided by the __Help Function__ - the user can click a button and launch a new browser window to that page. For those of us who create the Plugins - it's not quite that simple. On the bitweaver site - all of the Plugin Help Pages are named __))DataPluginX((__ where __X__ is the name of the plugin. When your Plugin is finished and you are ready to Commit it - visit ((DataPlugins)) and add the name of the Plugin to the list of Plugins. Make sure you provide the name of the page you specified here. Once the page is saved - create the Help Page and give plenty of examples how it is used. I like using the page snippets that I have already created while testing the Plugin. Look at the ((DataPluginSpyText|Spy Text Help Page)) to see what I mean.{DD}
-{DD title='description'}This should have been named __provide_a_very_brief_description__ because that is all that is needed here. Just a fast blurb to remind the user what this Plugin does.{DD}
-{DD title='help_function'}This specifies the name of the function that is called by bitweaver's Help Routines. I normally use a name like 'data_X_help' where X is the name of your plugin. __Note:__ Be sure to actually give the __Load Function__ the same name. Please don't laugh - I've made that mistake and at times it's hard to figure out why the stupid thing just isn't working. In some editors it's hard to see if there are 1 or 2 underline character.{DD}
-
-{DD title='syntax'}This should provide the __Tag__ and show all of the parameter names. e value given here {DD}
-
-{DD title='plugin_type'}This will always be __DATA_PLUGIN__ and should not be changed.{DD}
+This section is __Mandatory__ and should be edited with care. In the __$pluginParams Array__ : The first part is the index ('tag') and the second part is it's value ('COMMENT'). Only the values should be changed.
+{DD title='The GUID'}The Liberty System uses a GUID's (Globally Unique IDentifier) for storing & retrieving data and acts in a manner very similar to the Registry created by MS Windows. It uses GUID's for locating Plugin information as well.
+The __Define__ on Line 18 starts this off by storing a value (in this case 'comment') that is used to create the GUID. The __$pluginParms__ Array (Line 20 thru 30) contains the information to be stored. Line 31 Registers the Plugin with the Liberty System & Line 32 Registers the Plugin's __Tag__. The last is important because it is the __Tag__ that is looked up when the page is displayed.{DD} {DD title='Note the Comments'}When creating a new Plugin - each of the Comments (both upper & lower case) should be changed to the name of your new Plugin. The Capitalization should remain as it is.{DD} {DD title='tag'}The __Tag__ Controls what the user has to enter to make the Plugin work. Most of the time the __Tag__ should be the same as the Name & Title of the Plugin. Sometimes it makes more sense to use an abbreviated name instead. The Plugin ))DropDown(( is a good example. The Tag for it was shortened to __'DD'__ because I expected it to be used a lot - it creates the expandable boxes that you looking at.{DD} {DD title='auto_activate'}Every Plugin can be turned off in bitweaver by the Administrator - but the __'auto_activate'__ value provides a __Default Value__. This can come in handy because it gives lets you turn a Plugin Off or On while working on it without having to use the Admin Menu to make the change.{DD} {DD title='requires_pair'}This controls how the Plugin operates. When __))TRUE((__ all of the information needed by the Plugin is contained in a single Code Block. Plugins like __))AddTabs((__ and __))AgentInfo((__ operate with this model. When it is __))FALSE((__ - Two Code Blocks are needed (the First to Start the Plugin Running and the Second to Shut if Off). Usually - this type of Plugin operates on the Data located between the Code Blocks. Both __Comment__ and __))DropDown((__ operate with this model.{DD} {DD title='load_function'}This specifies the name of the function that does all the work and is called by bitweaver. I normally use a name like 'data_X' where X is the name of the Plugin. __Note__ Be sure to actually give the __Load Function__ the same name. Please don't laugh - I've made that mistake. In some editors it's hard to see if there are 1 or 2 underline character.{DD} {DD title='title'}The __Title__ is the "Unofficial" name of the Plugin and it is only used by Help Routine. If you created a Plugin called __"))AardvarkAreCute(("__ - you would probably specify the __Tag__ as __))AAC((__ and place __"Aardvark Are Cute (AAC)"__ in the __Title__. The latter provides a visual clue that a user shouldn't be able to miss.{DD} {DD title='help_page'}Every Plugin should have a Help Page on bitweaver. The value given ends up as a URL - so while viewing the limited Help provided by the __Help Function__ - the user can click a button and launch a new browser window to that page. For those of us who create the Plugins - it's not quite that simple. Here are the steps needed:
+*Update the ((DataPlugins)) page on bitweaver. All of the Plugin Help Pages are named __))DataPluginX((__ where __X__ is the name of the Plugin. So add a the Plugin to the Table and specify the page to be added. Make sure you provide the name of the page you specified here and save the page.
+* Create the Help Page and give plenty of examples how it is used. I like using the page snippets that I have already created while testing the Plugin. Look at the ((DataPluginSpyText|Spy Text Help Page)) to see what I mean.
+* Test the Plugin. Check to make sure that the Link to the Help Page actually finds the right page.
+* __Commit__ - The Plugin is Finished (Sounds of Applause)
+* The final item is not really required - but I do it just because it feels good. Send a __"Developers Update"__ email to __bitweaver-core@lists.sourceforge.net__ and let everybody know about the __Birth of your New Baby__.{DD} {DD title='description'}This should have been named ))__provide_a_very_brief_description_here__(( because that is all that is needed. Just a fast blurb to remind the user what this Plugin does.{DD} {DD title='help_function'}This specifies the name of the function that is called by bitweaver's Help Routines. I normally use a name like 'data_X_help' where X is the name of your Plugin. __Note:__ Be sure to actually give the __Help Function__ the same name. If the Help doesn't show up in the Page Editor - Look at that First!{DD} {DD title='syntax'}There was no reason to standardize the __Syntax__ before - but there soon will be. The __Syntax__ will be inserted into the page at the cursors position when the __Insert Button__ is clicked. For that reason - a Plugin that only needs one Code Block (__))requires_pair(( = ))FALSE((__) should look like this:
+~123~))COMMENT(( parameter= ~125~
+And a Plugin that Needs Two Code Blocks (__))requires_pair(( = ))TRUE((__) should look like this:
+~123~))COMMENT(( parameter= ~125~text~123~COMMENT~125~{DD} {DD title='plugin_type'}As stated earlier - the Liberty System has 3 different kinds of Plugins. The __'plugin_type'__ is used to let Liberty know the kind of Plugin that this is. Since this Tutorial is only concerned with __Liberty Data Plugins__ the value should __Always__ be __DATA_PLUGIN__{DD}
 
 !! The Help Function
 Help - how can it provide Help? . . . Oh yeah - there was that listing of Plugins at the bottom of the of the page in the editor. So that means that - each Plugin provides the data that is displayed by the bitweaver.
-{CODE source=php num=on }/*****************
+{CODE source=php num=33 }/*****************
  * Help Function *
  *****************/
 function data_comment_help() {

@@ -186,15 +191,18 @@

 }{CODE}
 {DD title='Line 4'}The name of the Help Function __Must__ match the name supplied in the Initialization Section's
 __'help_function'__{DD}
-__NOTE:__ As the creator of a plugin - you get to decide what how much information it provides. This Tutorial provides some Guidelines - but it is your responsibility to give your users the information they are going to need - so take pride in your accomplishment and explain it.
+__NOTE:__ As the creator of a plugin - you get to decide what how much information it provides. Lets Face Facts Ok? It is your responsibility to give the users the information they are going to need. Take some pride in your accomplishment - and spend the time needed to explain it. (Watch ))StarRider(( slowly climbing off his soap box.)
 
 !! The Load Function
-Each Plugin has 4 basic sections. They are:
-{CODE source=php num=on }/****************
+This is where all the Magic Happens. (Do you hear the sound of my laughter?)
+{CODE source=php num=52 }/****************
 * Load Function *
  ****************/
 function data_comment($data, $params) {
  return ' ';
 }{CODE}
+See __The Way It Works__ (Above)
 
-Good Luck
+I hope that you've enjoyed this little Tutorial.
+The Next Step is the __((Tutorial - Liberty Plugins II))__
+Good Luck - [http://www.bitweaver.org/StarRider|StarRider]
Page History
Date/CommentUserIPVersion
19 Jul 2005 (08:40 UTC)
Lee LaMont Bell Jr.68.95.129.2511
Current • Source
Lee LaMont Bell Jr.68.95.129.259
View • Compare • Difference • Source
Lee LaMont Bell Jr.68.95.129.258
View • Compare • Difference • Source
Lee LaMont Bell Jr.68.95.129.257
View • Compare • Difference • Source
Lee LaMont Bell Jr.68.95.129.256
View • Compare • Difference • Source
Lee LaMont Bell Jr.68.95.139.2165
View • Compare • Difference • Source
Lee LaMont Bell Jr.68.95.139.2163
View • Compare • Difference • Source
Lee LaMont Bell Jr.68.95.139.2161
View • Compare • Difference • Source