Integrating Flex into Ajax applications

Building Ajax applications has proven to be a consistent method for providing engaging applications. However, the explosion in popularity of Adobe Flex cannot be ignored. As we are continually pushed to create the best user experience, we're often faced with the difficult task of integrating Flash-based assets embedded in our Ajax applications. This article discusses the integration of Flash content with existing Ajax content using the FABridge, a code library developed by Adobe to handle this very task.

To be an Ajax developer today is pretty special. We're always at the front lines, ready to greet users and offer the best first impression to the applications we build for them. As Web standards advance and more vendors decide to implement them, our jobs have become easier, allowing us to focus on the user experience. The further advancements in JavaScript frameworks such as Ext JS, jQuery, and Prototype have also allowed us to spend less time worrying about whether our code will work across the platforms we're asked to support, leaving more time to innovate.

Although there are certainly more tools, techniques, and resources available to us today, there is also a shift in development methodology that serves as a push toward the rich world of Flash development. For many shops, the development workflow would involve the user interface (UI) group to produce designs that support a server-side-generated application. With just the JavaScript frameworks we have now, we're pushed in the direction of application development for the client side. However, the emergence of the Flex platform — a free, open source framework for producing Flash applications — brings us further into the application development arena. This type of innovation is good for us on the client side, but we must ensure that we handle the process of integrating it with current architectures in a thoughtful and careful manner.

Before I introduce code samples showing how to work with Ajax and Flex assets, you need to understand the tools and skills required:

  • I produced the Ajax samples in this article using the Ext JS JavaScript library, so you need to download the .zip file that contains the library and supporting documentation.
  • Next, grab a copy of the free Adobe Flex 3 SDK and Adobe Flash Player 9 with debugging capability, if you don't already have it.
  • Although not required to follow along in this article, you may also want to check out at least a trial version of Adobe Flex Builder 3, an Eclipse-based IDE that enables rapid Flex application development in addition to superior debugging and profiling capabilities (see Resources).
  • Finally, a working knowledge of PHP is helpful.

The integration issue

If you were looking forward to replacing all your Ajax content with Flex assets, your task would be much simpler. However, this is an unlikely and often unreasonable approach, because there are many reasons to preserve traditional Ajax functionality. Fortunately, there's no reason you can't keep the best of both environments to produce a rich, cohesive application.

There are quite a few simplistic methods for passing data to ActionScript code from the Flash container (HTML/JavaScript code), including the use of query strings and <param> tags. However, this method is limited to passing data into the container. A more powerful technique is to use the ExternalInterface class, an application program interface (API) used to broker communication between the ActionScript and JavaScript languages. The use of ExternalInterface is best demonstrated by the example in Listing 1:


Listing 1. ExternalInterface example
// ActionScript code
function exposed():String
{
   return "Hello, JavaScript!";
}

ExternalInterface.addCallback( "getActionScript", exposed );

// HTML/JavaScript code
<script language="JavaScript">

var result = flashObject.getActionScript();

</script>

<object id="flashObject" ...>
   <embed name="flashObject" ... />
</object>

Listing 1 demonstrates a stripped-down example of how to use the ExternalInterface class to register an ActionScript function so that JavaScript code can call it. You do this by first defining an ActionScript function, then using the addCallback() method to expose the function to JavaScript for execution. On the HTML side, simply obtain a handle to the Flash container and call the function, which was named using the first parameter to the addCallback() method. Although this demonstration concentrated on exposing functions to the JavaScript code, you can just as easily go the other way by using the call() method of the ExternalInterface class.

The ExternalInterface class can be quite powerful, but there are significant drawbacks to implementing it. To use ExternalInterface, you must be able to write code to implement both the ActionScript and JavaScript environments. This not only requires added skill but double the effort. In this situation, maintaining code as well as two very robust skill sets can become a challenge.

To address the limitations of development against the Flash external API, Adobe has released the FABridge. The FABridge, which ships with the Flex SDK, is a small library used to expose Flash content to scripting in the browser and works in most major browser platforms. With the FABridge, plumbing code that was required to directly implement the Flash external API is now virtually eliminated. Further, the skills required to implement the bridge aren't as robust. As a JavaScript developer, you simply need to be able to understand what's available to you in the way of ActionScript properties and methods. Let's get started with a few examples that demonstrate the capabilities of the FABridge.

An FABridge tutorial

Before you get started using the FABridge, here are the materials and development environment you'll be working with. After downloading the latest Flex SDK, configure the directory structure shown in Listing 2:


Listing 2. Directory structure for the FABridge tutorial
/
+--- bridge
|      +--- fabridge.js
|      +--- fabridge.as
|      
+--- index.html

The directory structure is straightforward: You just have an index page and the FABridge scripts hooked into their own directory named bridge. The location of the FABridge library files depends on your environment. Because I'm using Flex Builder 3 Professional on Mac OS X, my library files reside in install_root/sdks/frameworks/3.0.0/javascript/fabridge/.

Now that you have the appropriate architecture in place, you can begin creating the skeletons on both the HTML/JavaScript and ActionScript sides. Use the code from Listing 3 to develop the HTML/JavaScript skeleton:


Listing 3. HTML/JavaScript skeleton
<html>
<head>
<title>FABridge Tutorial</title>

<script type="text/javascript" src="bridge/FABridge.js"></script>
<script type="text/javascript">
   // ... 
</script>

</head>
<body>
</body>
</html>

As you can see, you simply hook the FABridge JavaScript library to your code, and all the functionality of the bridge is immediately available. Next, use the code from Listing 4 to implement the bridge on the ActionScript side:


Listing 4. Application skeleton
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
       xmlns:bridge="bridge.*"
       layout="absolute"
       width="300"
       height="142">
	   
   <bridge:FABridge bridgeName="flex" /> 
   <mx:TextInput x="70" y="54" id="txt_test" text="FABridge rocks!"/>
   
</mx:Application>

This code might be a bit more unfamiliar to you. The UI is kept clean and simple by defining a single text input control with the ID txt_test and a default value of FABridge rocks! The bridge namespace is defined, and all classes in the bridge directory are imported. Finally, the Flex application is given a name for the bridge to use to access it: flex. To compile this Flex code into a working SWF document, use the mxmlc utility from the Flex 3 SDK. The most basic compile command is shown in Listing 5:


Listing 5. Compiling MXML
path_to_flex_bin_folder/mxmlc path_to_mxml file

The command in Listing 5 compiles the source file and outputs an SWF file with the same file name as the MXML in the same directory. Assuming a successful compilation, you can now hook the resulting SWF into your HTML file, as shown in Listing 6:


Listing 6. Linking the resulting SWF file
<embed src=�main.swf� />

Note: The code in Listing 6 is deliberately light to keep focus on the task of demonstrating the FABridge. Unless you're targeting a specific environment (Listing 6 is targeting Mozilla), you'll want to add more intelligence in the way of object tags and other load scripts.

Assuming that all went well, your application should now look similar to Figure 1:


Figure 1. The sample application
Sample application

Now that you have successfully compiled and linked the Flex application into the HTML container, invoke your first FABridge functions to obtain a reference to the Flex application. Use the code in Listing 7 to fill in the empty <script> tag in your HTML skeleton file:


Listing 7. Obtaining a reference to the Flex application
// global variable, holds reference to the Flex application
var flexApp;  
  
var initCallback = function() {  
   flexApp = FABridge.flex.root();  
   return;  
}  
// register the callback to load reference to the Flex app
FABridge.addInitializationCallback( "flex", initCallback ); 

The code in Listing 7 starts by defining a global JavaScript variable that will hold a reference to the Flex application when the FABridge obtains it. A callback function is defined that sets the global variable and is invoked through the addInitializationCallback() FABridge method. Using this code is simply a matter of matching the name of the bridge that you configured in the Flex application. From here, you're able to access all sorts of ActionScript functionality from the JavaScript code.

Working with ActionScript objects

Now that you've obtained a global reference to the Flex application, you can access ActionScript objects through the consistent interface that the FABridge provides. In the ActionScript world, you would typically access objects through dot notation object.id. Rather than expose ActionScript objects in dot notation, however, the FABridge makes these objects available through function calls. It is a little different at first, but all you need to know is the template to follow. An object traditionally identified in ActionScript as object.id would now be accessed as object.getId(). This is best demonstrated through example: Type the code from Listing 8 into your HTML skeleton to try it out:


Listing 8. Getting ActionScript objects by ID
// get the text input control 
var txt = flexApp.getTxt_test();

The variable txt is an object that represents the text input control with the ID txt_test from the Flex application. You can see the template you would need to follow for gaining access to other ActionScript objects by ID. The declaration begins with the global reference to the Flex application, then a method call that always begins with the string get followed by the ID of the target object. Notice that the name of the ID must begin with a capital letter in this declaration.

Getting and setting the properties of ActionScript objects is similar to the process just used. Keeping up with our example of manipulating the text input control, use the code from Listing 9 to get and set the text property:


Listing 9. Get and set ActionScript properties
alert( "old: " + txt.getText() );
	
txt.setText( "Reset!" );
	
alert( "new: " + txt.getText() );

The code in Listing 9 first alerts the original value of the text input control from the Flex application. By following the template described earlier, you can see that the text property is obtained through a function call, with the get string prepended and the property name camel cased. The set() method uses the same process but accepts a parameter used to configure the new value of the object. After the code in Listing 9 executes, you should see a screen similar to Figure 2:


Figure 2. Setting ActionScript object properties
Setting ActionScript object properties

Now, let's move on to the easiest manipulation of all: calling ActionScript object methods. This process requires no special considerations on your part. ActionScript object methods are used in JavaScript code just as they would be used in ActionScript code. The code in Listing 10 demonstrates the invocation of a method on your text input control:


Listing 10. Invoking ActionScript methods
txt.setVisible( false );

The code in Listing 10 sets the text input control in the Flex application to be invisible. The object can still be referenced and manipulated, it's just not physically visible. Between the ActionScript and JavaScript worlds, this is no change in the way the methods are invoked.

One of the more powerful features of the FABridge is the ability to pass functions between JavaScript and ActionScript code. Check out the code in Listing 11, which dynamically copies the value of the text input in Flex to a <div> on the HTML/JavaScript side:


Listing 11. Passing functions
// define a function used as a callback to JavaScript
var txtCallback = function( event ) {  
   // get the object which fired the event
   var swf_source = event.getTarget();  
   
   document.getElementById( "copy" ).innerHTML = swf_source.getText();
	  
   return;  
}  
txt.addEventListener( "change", txtCallback );

The code in Listing 11 is a JavaScript callback function that's fired each time the text input control value from the Flex application changes. When the value changes, it is copied to a <div> tag with the ID copy. This type of functionality can be very powerful, especially when attempting any sort of integration work between Ajax and Flex content. With both environments relying heavily on events, it's key to be able to have them work together.

The last feature this article explores is exception handling. By default, when you use try . . . catch blocks throughout your JavaScript code, you'll be able to at least access an error code that you can then look up in the online reference for ActionScript errors. This methodology certainly works, but during development, you want access to as much information up front as possible. While using the FABridge, you can get this information simply by installing Flash Player 9 with debugging. With this feature installed, you have access to line numbers, file names, error types, and stack traces. Use the code in Listing 12 to see an example:


Listing 12. Exception handling
try {
   alert( flexApp.throwsAnError() );
}
catch( e ) {
   var msg = "";
   for( var i in e ) {
      msg += i + " = " + e[i];
   }
   alert( msg );
}

An error is thrown from the code in Listing 12 because the method throwsAnError() does not exist. The code from the catch block outputs an alert that looks similar to Figure 3:


Figure 3. Exception data
Exception data

As you can see, this data is far more useful than a single error code and less work to troubleshoot. When you're working with complex integration issues between differing technologies such as JavaScript and ActionScript, you'll appreciate this extra help.


Putting it all together

So far, this article has taken a tutorial-type approach to showing the capabilities of the FABridge. Now it's time to use a real-world scenario to demonstrate its usefulness. As indicated earlier, you want to integrate and use the best of both the Ajax and Flex worlds. One of the components that really shines on the Flex side is its charting capability. Although it's not a free library, it is worth the added cost if you're looking to do some intense client-side application programming. The example you'll work with here is a combination of a PHP service that serves dummy data containing the number of messages received in certain categories for specific users. The data from the PHP service is loaded into a grid control using the Ext JS JavaScript framework, then the same data is pushed over the FABridge as a data provider to a pie chart in Flex. Start by taking at look at the PHP service in Listing 13:


Listing 13. The PHP service
<?php
   $users = array();
   array_push( $users, "Paul" );
   array_push( $users, "Nancy" );
   array_push( $users, "Ned" );
   array_push( $users, "Lucy" );

   $email = array();
   $email[ "email" ] = array();
   $email[ "totalCount" ] = count( $users );
	
   for( $i = 0; $i < count( $users ); $i++ ) {
      $tmp = array();
      $tmp[ "user" ] = $users[$i];
      $tmp[ "friends" ] = rand( 0, 100 );
      $tmp[ "family" ] = rand( 0, 100 );
      $tmp[ "spam" ] = rand( 0, 100 );
      array_push( $email[ "email" ], $tmp );
   }
	
   echo json_encode( $email );
?>

Note: The data in this service is hard coded and only meant to demonstrate the concept.

Next, take a look at Listing 14, which is the MXML to generate the pie chart:


Listing 14. Flex pie chart
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:bridge="bridge.*"
                width="600"
                height="800">
   <bridge:FABridge bridgeName="flex" />
   <mx:Script>
   <![CDATA[    

      [Bindable]
      public var email:Object;
    
      private function displaySpam( data:Object,
                                    field:String,
                                    index:Number,
                                    percentValue:Number ):String {
         var temp:String= (" " + percentValue).substr(0,6);
         return data.user + ": " + '\n' + "Total Spam: " + data.spam + '\n' + temp + "%";
      }
   ]]>
   </mx:Script>


   <mx:Panel title="Spam E-mail">
      <mx:PieChart id="chart" 
                   height="100%" 
                   width="100%"
                   paddingRight="5" 
                   paddingLeft="5" 
                   showDataTips="true" 
                   dataProvider="{email}">          
         <mx:series>
            <mx:PieSeries nameField="user"
                          labelPosition="callout" 
                          field="spam" 
                          labelFunction="displaySpam">
            </mx:PieSeries>
         </mx:series>
      </mx:PieChart>  
      <mx:Legend dataProvider="{chart}"/>
   </mx:Panel>
</mx:Application>

This code was taken from the Adobe documentation and modified to fit the scenario as well as configured for use with the FABridge. One thing to note here is that the variable named email is bindable, which means that any references to this data set will be updated automatically. This works great, because you'll be sending data over the bridge to this same variable, which is then used as the data provider to the pie chart.

The last piece to this puzzle is the JavaScript code in Listing 15:


Listing 15. Produce the grid, populate the chart
<link rel="Stylesheet"
         type="text/css"
         href="../global/ext-2.0.2/resources/css/ext-all.css" />

<script type="text/javascript" src="bridge/FABridge.js"></script>
<script type="text/javascript" src="../global/ext-2.0.2/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../global/ext-2.0.2/ext-all-debug.js"></script>
<script type="text/javascript">

Ext.onReady( function() {
   // global variable, holds reference to the Flex application
   var flexApp;  
	  
   var initCallback = function() {  
      flexApp = FABridge.flex.root(); 
		
      // load the data store, grid, populate the pie chart from JavaScript
      initUI();
		 
      return;  
   }  

   // register the callback to load reference to the Flex app
   FABridge.addInitializationCallback( "flex", initCallback );
	
   function initUI() {
	
      // create a data store using the PHP service
      var ds_email = new Ext.data.JsonStore(
         {
            url: "service.php",
            root: "email",
            fields: [ "user", "family", "friends", "spam" ]
         }
      );
		
      // when the data store loads, update the pie chart
      ds_email.load( 
         {
            callback: function( r, o, s ) {
               // send an array of objects over the bridge to fill in the pie chart
               var arr_email = new Array();
					
               for( var i = 0; i < r.length; i++ ) {
                  var tmp = new Object();
                  tmp.user = r[i].data.user;
                  tmp.family = r[i].data.family;
                  tmp.friends = r[i].data.friends;
                  tmp.spam = r[i].data.spam;
						
                  arr_email.push( tmp );
               }
					
               flexApp.setEmail( arr_email );	
            }
         }
      );
		
      // create a populate the grid.
      var grid_email = new Ext.grid.GridPanel(
         {
            title: "E-mail Count",
            store: ds_email,
            columns: [
               { header: "User", dataIndex: "user" },
               { header: "Family", dataIndex: "family" },
               { header: "Friends", dataIndex: "friends" },
               { header: "Spam", dataIndex: "spam" }
             ],
             height: 300,
             width: 450,
             renderTo: "grid-email"
         }
      );	
   }
} );

</script>

The first thing to notice about this code are the links to the Ext JS resources needed to make it work. After hooking in the default styles and debug scripts for Ext, an onReady block is configured. This block is executed only after a full Document Object Model (DOM) is ready. You should be familiar with the code used to populate the global flexApp variable with a reference to the Flex application. One addition to the callback is the execution of the initUI function. This function is used to create an Ext data store using the PHP service and to populate an Ext grid control using the resulting data in the store. When the Ext data store is loaded, a data structure is created and pushed over the FABridge so that the data binds as a data provider to the pie chart. The final product is shown in Figure 4:


Figure 4. The final product
Final product

As you scan the data in the grid, it should match up with what's represented in the pie chart. This really is a powerful concept, and you can see the possibilities it has to offer.

Although this was a single real-world example of how you might want to implement the FABridge, there are several other popular ways to use this library. Syncing security information for remote service authentication and techniques to consistently brand and personalize applications are just a couple of examples of how best to use the bridge.


Conclusion

Adobe Flex is an incredible technology that is just starting to reveal its true potential. However, no single product will solve all the wants and needs of developers and users, so it's important that we keep our minds open and explore the possibilities of integration using the FABridge.


Resources

Learn

Get products and technologies

  • Adobe Flex: Visit the Flex product page.
     
  • Ext JS: Download the Ext JS JavaScript framework.
     

Discuss

About the author

Brice Mason

Brice Mason is a husband and father from Albany, New York. He is also a developer, writer, and speaker who frequently starts sentences with "Wouldn't it be cool if . . . ." When not spending time with his family, he can be found writing code and writing about code.

ref:www.ibm.com/developerworks/web/library/wa-aj-flex/#ibm-pcon

Flex的MXML文件结构

       下面是一个MXML

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout="absolute">

   

</mx:Application>

 

第一行声明XML文件采用的语法版本号和文件采用的编码格式。mx:Application标签是一个特殊的标签。在每一个MXML文件,但作为程序入口的运行文件只有一个,主文件的标示是根节点为mx:Application,一个程序中出现一个mx:Application节点。还有一个属性xmlns:mx=http://www.adobe.com/2006/mxml表示将mx定义为XML的命名空间。xmlns标签专门用来定义XML的命名空间,XML命名空间可以用来定义一套独立的XML标签,并且为这些标签指定特殊的解析方式。比如XML文件中默认的标签格式为:<Button>node</Button>,这里的Button节点作为一个普通的文本节点,没有特殊的意义。定义命名空间后,在节点上加上空间前缀:<mx:Button></mx:Button>这时候就代表mx空间下的Button对象。

Mx命名空间对应的路径是”http:// www.adobe.com/2006/mxmlFlex的配置文件中将这个路径定义为一个全局资源标识符,并对应了一个XML文件。在这个文件中,列出了mx命名空间下的所有标签。在Flex SDK3.0.1 目录下的frameworks目录中找到flex-config.xml文件,打开并找到如下内容:

 

<namespaces>

     <namespace>

            <uri>http://www.adobe.com/2006/mxml</uri>

            <manifest>mxml-manifest.xml</manifest>

     </namespace>

</namespaces>

 

从上面可以发现,http://www.adobe.com/2006/mxml”这个URImxml-manifest.xml文件对应,打开该文件,该文件列出了MXML中的所有标签与标签关联的类文件路径。

<?xml version="1.0"?>

 

<componentPackage>

 

   <component id="FileSystemComboBox" class="mx.controls.FileSystemComboBox"/>

<component id="FileSystemDataGrid" class="mx.controls.FileSystemDataGrid"/>

      。。。。。。。

<component id="WebService" class="mx.rpc.soap.mxml.WebService"/>

<component id="WebServiceOperation" class="mx.rpc.soap.mxml.Operation"/>

 

</componentPackage>

 

 

在开发中,当程序有很多MXML文件和AS文件时,为了方便调用,我们可以将功能相似的文件放在一个文件夹中,定义一个命名空间在定义命名空间时,为了方便,很一般直接指定命名空间包括的标签路径。比如:

xmlns:myComp=”components.*”

上面使用了通配符”*” components目录下所有的MXML文件个ActionScript类文件都被包括在myComp命名空间下。例如components中有一个Loginpanel.mxml,则程序中调用这个文件时,代码如下

<myComp: Loginpanel></myComp: Loginpanel>

myComp下的标签被自动指向components文件,当标签数量较多而且分布在不同文件夹时,可以效仿Flex配置文件的做法,使用XML文件来描述。

ref:hi.baidu.com/flexok/blog/item/83397ca93c70dc044a36d61b.html

用 Javascript 获取指定页面元素的位置 - top, left, document, Element, 元素, position, 位置

 

 Javascript 获取指定页面元素的位置是一个非常常见的需求,本文介绍的方法能够准确返回一个元素相对于整个文档左上角的坐标,即元素的 top left 的位置,而且能够兼容浏览器,相信对新手非常有用。

 

 

--------------------------------------------------------------

点此浏览示例文件

--------------------------------------------------------------

 

 

Javascript:

 

  1.  
  2. <script language="JavaScript" type="text/javascript">
  3. <!--
  1.  
  1. // 说明:用 Javascript 获取指定页面元素的位置
  2. // 整理:http://www.codebit.cn
  3. // 来源:YUI DOM
  1.  
  1. function getElementPos(elementId) {
  1.  
  1. var ua = navigator.userAgent.toLowerCase();
  2. var isOpera = (ua.indexOf('opera') != -1);
  3. var isIE = (ua.indexOf('msie') != -1 && !isOpera); // not opera spoof
  1.  
  1. var el = document.getElementById(elementId);
  1.  
  1. if(el.parentNode === null || el.style.display == 'none') 
  1. {
  1. return false;
  1. }
  1.  
  1. var parent = null;
  2. var pos = [];
  1. var box;
  1.  
  1. if(el.getBoundingClientRect)    //IE
  1. {
  1. box = el.getBoundingClientRect();
  1. var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
  2. var scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
  1.  
  1. return {x:box.left + scrollLeft, y:box.top + scrollTop};
  1. }
  1. else if(document.getBoxObjectFor)    // gecko
  1. {
  1. box = document.getBoxObjectFor(el);
  1. var borderLeft = (el.style.borderLeftWidth)?parseInt(el.style.borderLeftWidth):0;
  1. var borderTop = (el.style.borderTopWidth)?parseInt(el.style.borderTopWidth):0;
  1.  
  1. pos = [box.x - borderLeft, box.y - borderTop];
  1. }
  1. else    // safari & opera
  1. {
  1. pos = [el.offsetLeft, el.offsetTop];
  1. parent = el.offsetParent;
  1. if (parent != el) {
  2. while (parent) {
  1. pos[0] += parent.offsetLeft;
  2. pos[1] += parent.offsetTop;
  3. parent = parent.offsetParent;
  1. }
  1. }
  1. if (ua.indexOf('opera') != -1 
  1. || ( ua.indexOf('safari') != -1 && el.style.position == 'absolute' )) 
  1. {
  1. pos[0] -= document.body.offsetLeft;
  1. pos[1] -= document.body.offsetTop;
  1. } 
  2. }
  1. if (el.parentNode) { parent = el.parentNode; }
  1. else { parent = null; }
  1. while (parent && parent.tagName != 'BODY' && parent.tagName != 'HTML') 
  1. { // account for any scrolled ancestors
  1. pos[0] -= parent.scrollLeft;
  1. pos[1] -= parent.scrollTop;
  1. if (parent.parentNode) { parent = parent.parentNode; } 
  1. else { parent = null; }
  1. }
  1. return {x:pos[0], y:pos[1]};
  1. }
  1.  
  1. //-->
  1. </script>
  1.  

 

从 <http://www.codebit.cn/pub/html/javascript/tip/get_element_position/> 插入

VIM LaTeX Suite 正向反向搜索配置

 

本页主要讲述安装 VIM LaTeX Suite 后, 如何配置正向、反向查找。 关于 VIM LaTeX Suite 的下载地址, 可以在 VIM 常用插件简介页面中找到, 安装步骤请参看说明书, 这里不再详细解说。

1 简介

正向搜索指的是在生成的 dvi 文件中 查找 LaTeX 源代码中指定位置所对应的 dvi 段落、图表或其它元素; 反向搜索指的是在生成的 dvi 文件中查找指定段落、图表或其它元素 所对应源代码的位置。

众所周知, LaTeX 并不是一个“所见即所得”的编辑环境, 因此许多时候我们需要通过反复编译、查看效果来调节我们编写的代码。 正向搜索可以为代码的调节工作带来极大便利 ──当我们修改了一段代码后, 只需要编译它, 然后通过正向搜索直接跳转到 dvi 中与该段对应的位置, 即可立即直接查看到这段代码的效果。

而反向搜索在校对稿件的时候尤其有用。 当发现了错别字或病句的时候, 可以通过 dvi 文件反向搜索源代码位置, 快速跳转到源代码中的相应位置, 以便迅速而方便地修改这个错误。

2 Linux 下针对 xdvi 的配置

2.1 修改 .tex 文件的打开方式

VIM 打开文件的时候, 为了提高效率, 会为文件生成一个 swap 文件, 因此同一个文件是不能在多个不同的 VIM 下重复打开的。 为了让反向搜索能够正常工作, 我们必须让 VIM 运行在 remote 模式下。 在这里, 我们通过修改 .tex 的打开方式来实现这一点。

KDE 下的修改方法:

X-tex

打开 Konqueror, 点“设置”→“配置 Konqueror”, 在弹出的对话框中选择“文件关联”, 在右侧的窗口中找到“已知类型”→“text/x-tex”, 如图所示。 如果不存在 text/x-tex 项的话, 就自己新建一个。 选中 x-tex, 在右侧窗口中点击“添加”, 然后在弹出的窗口中输入:

gvim --servername "latex-suite" --remote-silent "%f"
 

注意: VIM7 目前暂不支持为不同的文件生成不同的 ViewRule, 因此在 VIM7 下暂时不能使用为不同的文件选择不同的 servername 功能。 如果你使用 VIM7 的话, 这里的 servername 参数请使用 latex-suite。

点击“确定”后使用“上移”按钮, 将新建的打开方式移动到列表的第一位, 使它成为 .tex 文件的默认打开方式。 之后在 Konqueror 中双击 .tex 文件时, 将使用 remote 模式的 VIM 打开。 关于 remote 模式的详细介绍, 请参看 VIM 的用户手册:

:help remote
 

2.2 修改 .vimrc,支持 xdvi

作者仅在 TeXLive 2004 下测试过 xdvi 的搜索功能, 在低版本的 xdvi 下也许无法使用, 请尽量升级到最新版本。

为了能正常使用正向、反向搜索 需要让编译得到的 dvi 具有源代码的行号信息。 我们在调用 latex 的时候提供“-src-specials”参数 来生成带有行号信息的 dvi 文件。 在你的 .vimrc 中添加以下内容:

let g:Tex_CompileRule_dvi="latex -src-specials -interaction=nonstopmode $*"
 

为了能够使用反向搜索, 必须让 xdvi 知道当前编辑器是工作在 remote 模式下的 VIM, 以便在用户搜索的时候调用编辑器显示查找结果。 由于 remote 模式的 servername 是根据命令行确定的, 因此不同的文件 servername 不一样, 如何在 VIM 里识别这个名字就成了一个问题。 滇狐目前使用 autocommand 的形式获取这个参数, 比较不优雅, 有很重的“hack”的痕迹。 如果有更好的主意, 请务必与滇狐联系。 在你的 .vimrc 中添加以下内容:

function RemoteLaTeX()
    let g:Tex_ViewRule_dvi="xdvi -editor 'gvim --servername \"".expand("%:p")
        \."\" --remote-silent'"
    augr remotelatex
    au!
    augr END
endfunction
 

配置完毕后存盘退出, 现在 VIM LaTeX 套装就能够配合 xdvi 工作了。

2.3 命令行工具

使用这样的方式开启 gVim, 需要在命令行给出文件的全路径, 以便正确设置 servername。 如果在 Konquerer 里双击打开, 它把文件的全路径传递给 gVim, 能够正常工作。 但如果在命令行下启动 gVim 的话, 手工设置正确的命令行就比较不容易了。 因此, 如果在命令行下编辑 LaTeX 文档, 请使用这段 Shell 脚本启动 gVim:

#!/bin/sh

if [ $# -lt 1 ]; then
    echo Usage: gvim-latex filename
else
    for i in "$@"
    do
        gvim --servername latex-suite --remote-silent "$i"
    done
fi
 

使用方法是:

gvim-latex my-latex-file.tex
 

2.4 使用方法

通过双击 .tex 文件的方式打开 remote 模式的 VIM (如果觉得有必要的话, 可以再写一个 sh 脚本, 用来启动 remote 模式的空 VIM), 然后编辑 .tex 文件。 编辑完毕后在 normal 模式下按“\ll”命令编译文件。 编译成功后, 将光标移动到想要查看效果的 LaTeX 源代码上方, 然后在 normal 模式下按“\ls”, VIM 会打开一个 xdvi, 并自动跳转到该源代码所对应的位置上。 在 xdvi 中按住 Ctrl 键单击页面元素, 便可在 VIM 中自动跳转到相应的源代码处。 反向搜索时,最好展开 VIM 中的所有 folder。

3 Windows 下针对 yap 的配置

3.1 修改 .tex 文件的打开方式

打开资源管理器, 点击“工具”→“文件夹选项”, 在弹出的对话框中选择“文件类型”。 在“已注册的文件类型”列表中任意选择一个, 然后在键盘上快速输入“tex”, 定位到 .tex 文件的打开方式项上。 如果不存在 .tex 文件类型的话, 点击“新建”创建一个。

选中 .tex 文件类型后, 点击窗口下方的“高级”按钮, 在弹出的窗口中修改默认打开方式的命令行。 如果所有打开方式均没有设为默认的话, 则“open”为默认打开方式。 选中要编辑的打开方式, 单击“编辑”按钮, 在弹出的窗口中输入命令行为:

gvim.bat --servername "%1" --remote-silent "%1"
 

这里使用文件的全路径作为 servername, 这样编辑不同的文件就可以使用不同的 gVim 而不用担心出现任何冲突, 感谢华宇煜提出这个创意。

注意: VIM7 目前暂不支持为不同的文件生成不同的 ViewRule, 因此在 VIM7 下暂时不能使用为不同的文件选择不同的 servername 功能。 如果你使用 VIM7 的话, 这里的 servername 参数请使用 latex-suite。

单击确定后退出文件夹选项对话框, 之后双击 .tex 文件后将使用工作在 remote 模式的 VIM 打开。

WinTeX

3.2 修改 _vimrc,生成搜索数据

为了能正常使用正向、反向搜索需要让编译得到的 dvi 具有源代码的行号信息。 我们在调用 latex 的时候提供“-src-specials”参数 来生成带有行号信息的 dvi 文件。在你的 .vimrc 中添加以下内容:

let g:Tex_CompileRule_dvi="latex -src-specials -interaction=nonstopmode $*"
 

3.3 修改 yap 配置,支持 VIM

打开 yap, 点击菜单“View”→“Options”, 在弹出的对话框中选择“Inverse Search”选项页, 在“Command”下方的文本框中写上:

gvim.bat --servername "%f" --remote-silent +%l "%f"
 

“确定”后就可以使用 yap 的反向搜索功能了。

注意: VIM7 目前暂不支持为不同的文件生成不同的 ViewRule, 因此在 VIM7 下暂时不能使用为不同的文件选择不同的 servername 功能。 如果你使用 VIM7 的话, 这里的 servername 参数请使用 latex-suite。

无须配置正向搜索,VIM LaTeX Suite 里已经内置了正向搜索的相关配置。

Yap

3.4 使用方法

通过双击 .tex 文件的方式打开 remote 模式的 VIM (如果觉得有必要的话, 可以再写一个批处理文件, 用来启动 remote 模式的空 VIM), 然后编辑 .tex 文件。 编辑完毕后在 normal 模式下按“\ll”命令编译文件。 编译成功后, 将光标移动到想要查看效果的 LaTeX 源代码上方, 然后在 normal 模式下按“\ls”, VIM 会打开一个 yap, 并自动跳转到该源代码所对应的位置上。 在 yap 中双击页面元素, 便可在 VIM 中自动跳转到相应的源代码处。 反向搜索时, 最好展开 VIM 中的所有 folder。

4 综述

VIM LaTeX Suite 是一套非常优秀的插件, 具有很强的可扩展性。 如果你使用的 dvi 查看器不在上面列出的范围之内, 只要查看器支持, 都可以通过相关配置很方便地整合到 VIM 中。

 

从 <http://edyfox.codecarver.org/html/vimlatex.html> 插入

100 Vim commands every programmer should know

 

Since the 70's, Vi is one of the programmer's best friend. Nevermind you're new to Vi or not, here's a big list of 100 useful commands, organized by topic, which will make your coder life better.

Basics

:e filename

Open filename for edition

:w

Save file

:q

Exit Vim

:w!

Exit Vim without saving

Search

/word

Search word from top to bottom

?word

Search word from bottom to top

/jo[ha]n

Search john or joan

/\< the

Search the, theatre or then

/the\>

Search the or breathe

/\< the\>

Search the

/\< ¦.\>

Search all words of 4 letters

/\/

Search fred but not alfred or frederick

/fred\|joe

Search fred or joe

/\<\d\d\d\d\>

Search exactly 4 digits

/^\n\{3}

Find 3 empty lines

:bufdo /searchstr/

Search in all open files

Replace

:%s/old/new/g

Replace all occurences of old by new in file

:%s/old/new/gw

Replace all occurences with confirmation

:2,35s/old/new/g

Replace all occurences between lines 2 and 35

:5,$s/old/new/g

Replace all occurences from line 5 to EOF

:%s/^/hello/g

Replace the begining of each line by hello

:%s/$/Harry/g

Replace the end of each line by Harry

:%s/onward/forward/gi

Replace onward by forward, case unsensitive

:%s/ *$//g

Delete all white spaces

:g/string/d

Delete all lines containing string

:v/string/d

Delete all lines containing which didnt contain string

:s/Bill/Steve/

Replace the first occurence of Bill by Steve in current line

:s/Bill/Steve/g

Replace Bill by Steve in current line

:%s/Bill/Steve/g

Replace Bill by Steve in all the file

:%s/\r//g

Delete DOS carriage returns (^M)

:%s/\r/\r/g

Transform DOS carriage returns in returns

:%s#<[^>]\+>##g

Delete HTML tags but keeps text

:%s/^\(.*\)\n\1$/\1/

Delete lines which appears twice

Ctrl+a

Increment number under the cursor

Ctrl+x

Decrement number under cursor

ggVGg?

Change text to Rot13

Case

Vu

Lowercase line

VU

Uppercase line

g~~

Invert case

vEU

Switch word to uppercase

vE~

Modify word case

ggguG

Set all text to lowercase

:set ignorecase

Ignore case in searches

:set smartcase

Ignore case in searches excepted if an uppercase letter is used

:%s/\<./\u&/g

Sets first letter of each word to uppercase

:%s/\<./\l&/g

Sets first letter of each word to lowercase

:%s/.*/\u&

Sets first letter of each line to uppercase

:%s/.*/\l&

Sets first letter of each line to lowercase

Read/Write files

:1,10 w outfile

Saves lines 1 to 10 in outfile

:1,10 w >> outfile

Appends lines 1 to 10 to outfile

:r infile

Insert the content of infile

:23r infile

Insert the content of infile under line 23

File explorer

:e .

Open integrated file explorer

:Sex

Split window and open integrated file explorer

:browse e

Graphical file explorer

:ls

List buffers

:cd ..

Move to parent directory

:args

List files

:args *.php

Open file list

:grep expression *.php

Returns a list of .php files contening expression

gf

Open file name under cursor

Interact with Unix

:!pwd

Execute the pwd unix command, then returns to Vi

!!pwd

Execute the pwd unix command and insert output in file

:sh

Temporary returns to Unix

$exit

Retourns to Vi

Alignment

:%!fmt

Align all lines

!}fmt

Align all lines at the current position

5!!fmt

Align the next 5 lines

Tabs

:tabnew

Creates a new tab

gt

Show next tab

:tabfirst

Show first tab

:tablast

Show last tab

:tabm n(position)

Rearrange tabs

:tabdo %s/foo/bar/g

Execute a command in all tabs

:tab ball

Puts all open files in tabs

Window spliting

:e filename

Edit filename in current window

:split filename

Split the window and open filename

ctrl-w up arrow

Puts cursor in top window

ctrl-w ctrl-w

Puts cursor in next window

ctrl-w_

Maximise current window

ctrl-w=

Gives the same size to all windows

10 ctrl-w+

Add 10 lines to current window

:vsplit file

Split window vertically

:sview file

Same as :split in readonly mode

:hide

Close current window

:­nly

Close all windows, excepted current

:b 2

Open #2 in this window

Auto-completion

Ctrl+n Ctrl+p (in insert mode)

Complete word

Ctrl+x Ctrl+l

Complete line

:set dictionary=dict

Define dict as a dictionnary

Ctrl+x Ctrl+k

Complete with dictionnary

Marks

mk

Marks current position as k

˜k

Moves cursor to mark k

d™k

Delete all until mark k

Abbreviations

:ab mail mail@provider.org

Define mail as abbreviation of mail@provider.org

Text indent

:set autoindent

Turn on auto-indent

:set smartindent

Turn on intelligent auto-indent

:set shiftwidth=4

Defines 4 spaces as indent size

ctrl-t, ctrl-d

Indent/un-indent in insert mode

>>

Indent

<<

Un-indent

Syntax highlighting

:syntax on

Turn on syntax highlighting

:syntax off

Turn off syntax highlighting

:set syntax=perl

Force syntax highlighting

 

从 <http://www.catswhocode.com/blog/100-vim-commands-every-programmer-should-know> 插入