# How to Debug

Due to Apple's safety concerns, you cannot use debug tools to run breakpoint test currently. For this stage you can use commands like alert, showHUD to print your wanted debug information on screen.

# QuartzCore

The visual feedback that iOS devices give to users is based on QuartzCore Framework, by which you can achieve some beautiful animation effect. You can learn QuartzCore Framework if you feel your plugin needs some UI rendering.

# UIKit

The content below is from Apple's Developer Documentation.

The UIKit framework provides the required infrastructure for your iOS or tvOS apps. It provides the window and view architecture for implementing your interface, the event handling infrastructure for delivering Multi-Touch and other types of input to your app, and the main run loop needed to manage interactions among the user, the system, and your app. Other features offered by the framework include animation support, document support, drawing and printing support, information about the current device, text management and display, search support, accessibility support, app extension support, and resource management.

# Request an Official Signature

# How to Request

Please follow the format below to send an email to service@marginnote.com. Our technician will view your request and reply within 72 hours.

  • Subject: Plugin Signature Request-[Your Plugin Name]-[Your Forum ID]
  • (Example subject): Plugin Signature Request-MDXdict-Petter1925
  • Body: 1. Description of the function of your plugin. 2. Technotes.
  • Attachment: An uncertified packed .mnaddon file for testing purposes.

Please notice that our service team may not identify a request with a non-standard email format.

# Privileges and Functions of an Official Signature

Publish new versions to users immediately

An uncertified plugin requires users to download the new version manually in order to update. This is not beneficial for bug fixes in time. With an official signature, you can use Marginnote Server to publish an update to all terminal users, which helps them to upgrade their plugin to the newest version.

Notice that you need to apply for an official signature again (following the instructions above) for an upgrade, but meanwhile we can help to publish your upgrade for you. (Based on an identification of the new/old signature to identify installed users, and use iCloud Kit to publish.)

To be exempt from additional set-up

If your plugin is uncertified, users need to allow uncertified extensions in Settings-General-Extension) in order to use it. But with an official signature, users can directly install your plugin.

To be exempt from popup alerts

There is a popup alert for uncertified plugins every time when user opens Marginnote. Certified plugins do not cause any popup alert.

# Others

# Common Issues

# Analyzing Autotitle

You might be fully aware of the API contents above but are still not sure how to organize your own plugin. Here we provide an annotated version of main.js from Autotitle plugin. Read it carefully and combine with what you have learnt, we believe that you will soon be able to make your own plugin.

Without further ado, start working on it and realize your own idea!

You are always welcome to contact us if you notice a mistake in this context, or have some better ideas. We look forward to hearing from you!

JSB.newAddon = function(mainPath){//This is *JSBridge* method, which you don't need to concern.
    var newAddonClass = JSB.defineClass('AutoTitle : JSExtension', /*Instance members*/{
      //Window initialize
      sceneWillConnect: function() {//the aforementioned life cycle function. What you have coded under *sceneWillConnect* will be executed once the window is activated.
          self.webController = WebViewController.new();//Here we create an instance called *WebViewController*
      },
      //Window disconnect
      sceneDidDisconnect: function() {
      },
      //Window resign active
      sceneWillResignActive: function() {
      },
      //Window become active
      sceneDidBecomeActive: function() {
      },
      notebookWillOpen: function(notebookid) {//This is a life cycle of *notebookWillOpen*. The code within will be executed once the window is activated.
        //*NSNotificationCenter* is a class about notification
        //*defaultCenter*() is a class method in *NSNotificationCenter*, which returns the instance of this class.
        //*addObserverSelectorName*() is a method of adding an observer, which can be called after the instance is created.

        NSNotificationCenter.defaultCenter().addObserverSelectorName(self,'onProcessExcerptText:','ProcessNewExcerpt');
        //When *ProcessNewExcerpt* happens, i.e. there is a new excerpt, the event *onProcessExcerptText* will be triggered. You need to define the follow-up work of this event.
        NSNotificationCenter.defaultCenter().addObserverSelectorName(self,'onProcessExcerptText:','ChangeExcerptRange');
        //When *ChangeExcerptRange* happens,i.e. the excerpt is changed, the event *onProcessExcerptText* will be triggered.
        self.autotitle = NSUserDefaults.standardUserDefaults().objectForKey('marginnote_autotitle');
        //binding the plugin
      },
      notebookWillClose: function(notebookid) {
        //This is to close relevant notification once the notebook closes.
        NSNotificationCenter.defaultCenter().removeObserverName(self,'ProcessNewExcerpt');
        NSNotificationCenter.defaultCenter().removeObserverName(self,'ChangeExcerptRange');
      },
      documentDidOpen: function(docmd5) {
      },
      documentWillClose: function(docmd5) {
      },
      controllerWillLayoutSubviews: function(controller) {
      },
      queryAddonCommandStatus: function() {
        if(Application.sharedInstance().studyController(self.window).studyMode < 3)//to judge current mode, i.e. the plugin does not work in read-only mode
          return {image:'title.png',object:self,selector:'toggleAutoTitle:',checked:(self.autotitle?true:false)};
        return null;
      },
      //Clicking note
      onProcessExcerptText: function(sender){//the content of the event triggered before, which defines what needs to be executed once the excerpt is created or changed.
        if(!Application.sharedInstance().checkNotifySenderInWindow(sender,self.window))return;//only process the content in current window
        if(!self.autotitle)return;//to decide whether the plugin is effective
        var noteid = sender.userInfo.noteid;//fetch note's ID
        var note = Database.sharedInstance().getNoteById(noteid);//fetch the note via its ID
        if(note && note.excerptText && note.excerptText.length > 0 && note.excerptText.length <= 250 && !note.groupNoteId){//the note and the excerpt text exists, excerpt ungrouped and length between 0 and 250
          var timerCount = 0;//initialize timer
          NSTimer.scheduledTimerWithTimeInterval(1,true,function(timer){
            var text = note.excerptText.split("**").join("");//save processed excerpt text to *text*
            if(text && text.length){
              UndoManager.sharedInstance().undoGrouping('AutoTitle',note.notebookId,function(){
                note.noteTitle = text;//assign *text* to note's title
                note.excerptText = '';//clear excerpt
                Database.sharedInstance().setNotebookSyncDirty(note.notebookId);//sync to database
              });
              NSNotificationCenter.defaultCenter().postNotificationNameObjectUserInfo('RefreshAfterDBChange',self,{topicid:note.notebookId});
            }
            timerCount++;
            if(timerCount >= 4){
              timer.invalidate();
            }
          });
        }
      },
      toggleAutoTitle: function(sender) {//to decide whether the plugin is on/off. You can use this content with a slight change for your own plugin.
        var lan = NSLocale.preferredLanguages().length?NSLocale.preferredLanguages()[0].substring(0,2):'en';
        if(self.autotitle){
          self.autotitle = false;
          if(lan == 'zh') //for Chinese language
            Application.sharedInstance().showHUD('自动设置标题已关闭',self.window,2);
          else
            Application.sharedInstance().showHUD('Auto title is turned off',self.window,2);
        }
        else{
          self.autotitle = true;
          if(lan == 'zh') //for Chinese language
            Application.sharedInstance().showHUD('创建摘录后,摘录内容将自动被设置为笔记标题',self.window,2);
          else
            Application.sharedInstance().showHUD('After creating an excerpt, the excerpt will be automatically set as the note title',self.window,2);
        }
        NSUserDefaults.standardUserDefaults().setObjectForKey(self.autotitle,'marginnote_autotitle');
        Application.sharedInstance().studyController(self.window).refreshAddonCommands();
      },
    }, /*Class members*/{
      addonDidConnect: function() {
      },
      addonWillDisconnect: function() {
      },
      applicationWillEnterForeground: function() {
      },
      applicationDidEnterBackground: function() {
      },
      applicationDidReceiveLocalNotification: function(notify) {
      },
    });
    return newAddonClass;
  };