text:
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_PANNER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_PANNER_H_

#include "third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h"
#include "third_party/blink/renderer/platform/audio/fft_convolver.h"
#include "third_party/blink/renderer/platform/audio/hrtf_database_loader.h"
#include "third_party/blink/renderer/platform/audio/panner.h"
#include "third_party/blink/renderer/platform/platform_export.h"

namespace blink {

class PLATFORM_EXPORT HRTFPanner final : public Panner {
 public:
  HRTFPanner(float sample_rate,
             unsigned render_quantum_frames,
             HRTFDatabaseLoader*);
  ~HRTFPanner() override;

  // Panner
  void Pan(double azimuth,
           double elevation,
           const AudioBus* input_bus,
           AudioBus* output_bus,
           uint32_t frames_to_process,
           AudioBus::ChannelInterpretation) override;
  void PanWithSampleAccurateValues(double* azimuth,
                                   double* elevation,
                                   const AudioBus* input_bus,
                                   AudioBus* output_bus,
                                   uint32_t frames_to_process,
                                   AudioBus::ChannelInterpretation) override;

  void Reset() override;

  unsigned FftSize() const { return FftSizeForSampleRate(sample_rate_); }
  static unsigned FftSizeForSampleRate(float sample_rate);

  float SampleRate() const { return sample_rate_; }

  double TailTime() const override;
  double LatencyTime() const override;
  bool RequiresTailProcessing() const override;

  unsigned RenderQuantumFrames() const { return render_quantum_frames_; }

 private:
  // Given an azimuth angle in the range -180 -> +180, returns the corresponding
  // azimuth index for the database, and azimuthBlend which is an interpolation
  // value from 0 -> 1.
  int CalculateDesiredAzimuthIndexAndBlend(double azimuth,
                                           double& azimuth_blend);

  scoped_refptr<HRTFDatabaseLoader> database_loader_;

  float sample_rate_;

  // We maintain two sets of convolvers for smooth cross-faded interpolations
  // when then azimuth and elevation are dynamically changing.  When the
  // azimuth and elevation are not changing, we simply process with one
  // of the two sets.  Initially we use CrossfadeSelection1 corresponding to
  // m_convolverL1 and m_convolverR1.  Whenever the azimuth or elevation
  // changes, a crossfade is initiated to transition to the new position. So if
  // we're currently processing with CrossfadeSelection1, then we transition to
  // CrossfadeSelection2 (and vice versa).  If we're in the middle of a
  // transition, then we wait until it is complete before initiating a new
  // transition.

  // Selects either the convolver set (m_convolverL1, m_convolverR1) or
  // (m_convolverL2, m_convolverR2).
  enum CrossfadeSelection { kCrossfadeSelection1, kCrossfadeSelection2 };

  CrossfadeSelection crossfade_selection_;

  // azimuth/elevation for CrossfadeSelection1.
  int azimuth_index1_;
  double elevation1_;

  // azimuth/elevation for CrossfadeSelection2.
  int azimuth_index2_;
  double elevation2_;

  // A crossfade value 0 <= m_crossfadeX <= 1.
  float crossfade_x_;

  // Per-sample-frame crossfade value increment.
  float crossfade_incr_;

  FFTConvolver convolver_l1_;
  FFTConvolver convolver_r1_;
  FFTConvolver convolver_l2_;
  FFTConvolver convolver_r2_;

  AudioDelayDSPKernel delay_line_l_;
  AudioDelayDSPKernel delay_line_r_;

  AudioFloatArray temp_l1_;
  AudioFloatArray temp_r1_;
  AudioFloatArray temp_l2_;
  AudioFloatArray temp_r2_;
  unsigned render_quantum_frames_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_PANNER_H_


text:
**PART-A_写在前面**

- **Handler是什么**
>Handler是Android提供的:
>1. 一套更新UI的机制.
>2. 一套消息处理的机制,我们可以通过Handler发送和处理消息.

- **为什么使用Handler**
>Android在设计时,就封装了一套消息的创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,就会抛出异常信息.

- **Google为什么只设计了Handler来解决更新UI的问题**
>解决多线程并发问题. 假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么就会出现更新界面错乱. 但是如果都进行加锁处理的话,又会导致性能下降. 因此,产生了Handler的更新机制,不用去关心多线程的问题,所有的更新UI的操作,都是在主线程的消息队列当中去轮询处理.

**PART-B_Handler使用方法**

- **首先提出一个BUG**

	```
	new Thread(new Runnable() {
	    @Override public void run() {
	        SystemClock.sleep(1000);
	        tv.setText("hello catface");
	    }
	}).start();
	```
	上代码片会报异常:只有主线程能进行更新UI的操作
	**android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.**

	**解决办法如下**
	```
	new Thread(new Runnable() {
	    @Override public void run() {
	        SystemClock.sleep(1000);

			// 通过handler的post()方法在子线程中更新UI
	        handler.post(new Runnable() {
	            @Override public void run() {
	                tv.setText("hello catface");
	            }
	        });
	    }
	}).start();
	```
- **实现简单轮播图(本例使用ImageView,自行可用ViewPager)**

	- **图片轮播ImageRunnable实现**

		```
		private int[] images = {R.drawable.1, R.drawable.2, R.drawable.3};
		private int index; // 记录当前图片

		private Handler handler = new Handler();

		private ImageRunnable imageRunnable = new ImageRunnable();

		class ImageRunnable implements Runnable {

		    @Override public void run() {
		        index++;
		        index = index % 3;
		        // 向ImageView控件设置轮播图片
		        iv.setImageResource(images[index]);
		        handler.postDelayed(imageRunnable, 1000);
		    }
		}
		```

	- **主线程中开启图片轮播**

		```
		handler.postDelayed(imageRunnable, 1000);
		```

- **handler.sendMessage()的使用:传递int和Object对象**

	```
	private TextView tv;

	private Handler handler = new Handler() {
		// msg为发送来的携带数据的消息
	    @Override public void handleMessage(Message msg) {
	        super.handleMessage(msg);
	        tv.setText(msg.arg1 + "::" + msg.arg2 + "::" + msg.obj);
	    }
	};

	@Override protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.activity_handler);

	    tv = (TextView) findViewById(R.id.tv_handler);

	    new Thread(new Runnable() {
	        @Override public void run() {
	            SystemClock.sleep(2000);
	            // Message msg = new Message(); // 不推荐使用

	            // Message内部维护了Message池用于消息复用,避免消息对象过多浪费. 推荐使用Message.obtain()
	            Message msg = handler.obtainMessage(); // 获取系统消息对象

	            msg.arg1 = 111; // int类型
	            msg.arg2 = 222;
	            msg.obj = new Person("zhangsan", 99); // 向消息中添加对象

	            handler.sendMessage(msg); // 发送消息给Handler
	            // msg.sendToTarget(); // 效果同上
	        }
	    }).start();

	}

	class Person {
	    public String name;
	    public int age;

	    public Person(String name, int age) {
	        this.name = name;
	        this.age = age;
	    }

	    @Override public String toString() {
	        return name + "------" + age;
	    }
	}
	```

- **分析上述代码片中两处:**

	- **obtainMessage()源码分析:避免没必要的消息对象的创建**
	```
	public final Message obtainMessage() {
		return Message.obtain(this);
	}
	.
	.
	.
	public static Message obtain(Handler h) {
		Message m = obtain();
	    m.target = h; // target消息发送对象为Handler本身

	    return m;
	}
	.
	.
	.
	public static Message obtain() {
	    synchronized (sPoolSync) {
	        if (sPool != null) {
	            Message m = sPool;
	            sPool = m.next;
	            m.next = null;
	            sPoolSize--;
	            return m;
	        }
	    }
	    // 系统中没有Message对象时才会创建消息对象
	    return new Message();
	}
	```

	- **sendToTarget()源码分析**

		```
		public void sendToTarget() {
			// 调用Handler自己的sendMessage()方法,本质上一样
			target.sendMessage(this);
		}
		.
		.
		.
		public final boolean sendMessage(Message msg) {
			return sendMessageDelayed(msg, 0);
		}
		```

- **Handler消息的移除:removeCallbacks()**

	```
	handler.removeCallbacks(Runnable);
	```

- **Handler消息拦截:Callback接口**

	```
	private Handler handler = new Handler(new Handler.Callback() {
	    @Override public boolean handleMessage(Message message) {
	        ShowTool.show(HandlerActivity.this, "1111...");
	        return false; // true为截获Handler发送来的消息
	    }
	}) {
	    @Override public void handleMessage(Message msg) {
	        ShowTool.show(HandlerActivity.this, "2222...");
	    }
	};

	@Override protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.activity_handler);

	    new Thread(new Runnable() {
	        @Override public void run() {
	            handler.sendEmptyMessageDelayed(1, 2000);
	        }
	    }).start();
	}
	```

- **自定义与线程相关的Handler**

	```
	private Handler handler = new Handler(){
	    @Override public void handleMessage(Message msg) {
	        super.handleMessage(msg);
	        LogTool.d("HandlerThread", "main:" + Thread.currentThread());
	    }
	};

	class MyThread extends Thread {
	    public Handler handler;

	    @Override public void run() {
	        Looper.prepare(); // 子线程中必须必须必须创建Looper对象
	        handler = new Handler() {
	            @Override public void handleMessage(Message msg) {
	                LogTool.d("HandlerThread", "thred:" + Thread.currentThread());
	            }
	        };
	        Looper.loop();
	    }
	}

	@Override protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.activity_handler);

	    MyThread thread = new MyThread();
	    thread.start();
	    SystemClock.sleep(800);

	    thread.handler.sendEmptyMessage(1); // 子线程
	    handler.sendEmptyMessage(1); // 主线程
	}
	```

- **HandlerThread:解决多线程并发问题**

	- **先看存在BUG的代码片**

		```
		private Handler handler = new Handler() {
		    // 主线程的handleMessage方法中不要执行耗时操作,否则出现卡死状态
		    @Override public void handleMessage(Message msg) {
		        super.handleMessage(msg);
		        LogTool.d("HandlerThread", "UI..." + Thread.currentThread());
		    }
		};

		class MyThread extends Thread {
		    public Handler handler;

		    public Looper looper;
		    @Override public void run() {
		        Looper.prepare();
		        handler = new Handler() {
		            @Override
		            public void handleMessage(Message msg) {
		                LogTool.d("HandlerThread", "==..." + Thread.currentThread());
		            }
		        };
		        Looper.loop();
		    }
		}

		@Override protected void onCreate(Bundle savedInstanceState) {
		    super.onCreate(savedInstanceState);
		    setContentView(R.layout.activity_handler);

		    MyThread thread = new MyThread();
		    thread.start();

			// 空指针:主线程执行到此方法时,MyThread类中Looper对象还没有创建成功
		    handler = new Handler(thread.looper) {
		        @Override public void handleMessage(Message msg) {

		        }
		    };

		    handler.sendEmptyMessage(1);
		}
		```
>上代码片会报异常:MessageQueue空指针
>**java.lang.RuntimeException: Unable to start activity ComponentInfo{com.catface/com.catface.demos.HandlerActivity}: java.lang.NullPointerException: Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue' on a null object reference**

		>异常分析见代码片**`handler = new Handler(thread.looper)`**

	- **解决办法:通过HandlerThread**

		```
		// 解决多线程并发问题 --> 将耗时操作交给子线程
		thread = new HandlerThread("handler thread");
		thread.start();

		handler = new Handler(thread.getLooper()) { ...
		```

- **主线程与子线程之间的信息交互**

	>主线程和子线程互相定时发送消息给对方,通知对方运行相关逻辑.

	```
	private Handler threadHandler; // 子线程Handler

	private Handler handler = new Handler() { // 主线程Handler
	    @Override public void handleMessage(Message msg) {

	        Message message = new Message();
	        LogTool.d("threadchat", "main handler");
	        threadHandler.sendMessageDelayed(message, 1000); // 主线程给子线程发送消息
	    }
	};

	@Override protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.activity_handler);

	    // 解决Looper空指针异常
	    HandlerThread handlerThread = new HandlerThread("handler thread");
	    handlerThread.start();

	    // 通过HandlerThread来解决线程并发问题
	    threadHandler = new Handler(handlerThread.getLooper()) {
	        @Override public void handleMessage(Message msg) {
	            Message message = new Message();
	            LogTool.d("threadchat", "thread handler");
	            handler.sendMessageDelayed(message, 1000); // 子线程给主线程发送消息
	        }
	    };

	    // 开始主子线程间的交互,互相定时发送消息给对方
	    findViewById(R.id.bt_start).setOnClickListener(new View.OnClickListener() {
	        @Override public void onClick(View view) {
	            handler.sendEmptyMessage(1);
	        }
	    });

	    findViewById(R.id.bt_end).setOnClickListener(new View.OnClickListener() {
	        @Override public void onClick(View view) {
	            handler.removeMessages(1);
	        }
	    });
	}
	```
	- **重要结论**
1. 子线程向主线程发送消息,必须有主线程的Handler实例,该Hnadler绑定了主线程的Looper.
2. 主线程向子线程发送消息,必须有子线程的Looper实例,为了防止Looper没有初始化,所以通过HandlerThread类,来保证子线程的Looper在被主线程调用时已经初始化.

- **更新UI的四种方式**

	- **post**

		```
		new Thread(new Runnable() {
		    @Override public void run() {
			    // post中sendMessageDelayed(getPostMessage(r), 0)
			    // get中调用通过Message.obtain()、callback(Runnable对象)
		        handler.post(new Runnable() {
		            @Override
		            public void run() {
		                ...
		            }
		        });
		    }
		}).start();
		```

	- **sendMessage:最常用**
	- **runOnUiThread():非UI线程就用Handler的post发送消息**
	- **View对象的post方法**
	```
	View.post(new Runnable() {
		run() {
			...
	    }
	})
	```

- **常见BUG:子线程创建Handler对象**

	```
	new Thread(new Runnable() {
	    @Override public void run() {
	        Looper.prepare(); // 创建Handler之前必须创建Looper对象
	        Handler handler = new Handler();
	    }
	}).start();
	```

	>若未创建Looper对象,而直接创建Handler对象,则会抛出异常
	>**java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()**

- **非UI线程真的不能进行更新UI的操作吗?(否)**

	>更新UI时会调用invalidate() --> viewParent.invalidateChild() --> invalidateChildInParent().
	>viewRootImpl()方法会检测当前是否为非UI线程,而该方法在Activity的onResume()中执行. 所以在onCreate()的子线程中更新UI速度很快时,viewRootImpl()方法还未执行,所以就不会报异常.

**PART-C_Handler源码简介**

>**Looper创建MessageQueue消息队列,然后无限轮询该MessageQueue中的消息,而消息的创建者即为Handler.**

- **Looper源码**

	- **构造函数**

		```
		private Looper(boolean quitAllowed) {
		    mQueue = new MessageQueue(quitAllowed);
		    mRun = true;
			mThread = Thread.currentThread();
		}
		```
>创建了一个MessageQueue对象(消息实例).

	- **prepare()**

		```
		public static final void prepare() {
			if (sThreadLocal.get() != null) {
				throw new RuntimeException("Only one Looper may be created per thread");
			}
			sThreadLocal.set(new Looper(true));
		}
		```
>将Looper实例存储在线程中,并保证线程中只有一个Looper实例.

	- **loop()**

		```
		public static void loop() {
		    final Looper me = myLooper();
		    if (me == null) { // loop方法必须在prepare方法之后运行
		        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
		    }
		    final MessageQueue queue = me.mQueue; // 拿到消息队列

		    // Make sure the identity of this thread is that of the local process,
		    // and keep track of what that identity token actually is.
		    Binder.clearCallingIdentity();
		    final long ident = Binder.clearCallingIdentity();

			/**
			 * 无限循环
			 */
		    for (;;) {
		        Message msg = queue.next(); // 取消息,若没有则阻塞
		        if (msg == null) {
		            // No message indicates that the message queue is quitting.
		            return;
		        }

		        // This must be in a local variable, in case a UI event sets the logger
		        Printer logging = me.mLogging;
		        if (logging != null) {
		            logging.println(">>>>> Dispatching to " + msg.target + " " +
		                    msg.callback + ": " + msg.what);
		        }

				// 将消息交给target(handler对象)的dispatchMessage方法处理
		        msg.target.dispatchMessage(msg);

		        if (logging != null) {
		            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
		        }

		        // Make sure that during the course of dispatching the
		        // identity of the thread wasn't corrupted.
		        final long newIdent = Binder.clearCallingIdentity();
		        if (ident != newIdent) {
		            Log.wtf(TAG, "Thread identity changed from 0x"
		                    + Long.toHexString(ident) + " to 0x"
		                    + Long.toHexString(newIdent) + " while dispatching to "
		                    + msg.target.getClass().getName() + " "
		                    + msg.callback + " what=" + msg.what);
		        }

		        msg.recycle(); // 释放消息占据的资源
		    }
		}
		```
>Looper的主要作用如下
>1. 绑定当前线程,保证一个线程仅有一个Looper实例,同时一个Looper实例中仅有一个MessageQueue实例.
>2. loop()方法,轮询MessageQueue中的消息,交给消息的target属性的dispatchMessage处理.

- **Handler源码**

	- **构造函数**
		```
		public Handler(Callback callback, boolean async) {
		    if (FIND_POTENTIAL_LEAKS) {
		        final Class<? extends Handler> klass = getClass();
		        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
		                (klass.getModifiers() & Modifier.STATIC) == 0) {
		            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
		                klass.getCanonicalName());
		        }
		    }

			// 获取当前线程保存的Looper实例
		    mLooper = Looper.myLooper();
		    if (mLooper == null) {
		        throw new RuntimeException(
		            "Can't create handler inside thread that has not called Looper.prepare()");
		    }
		    // 获取Looper实例中保存的MessageQueue()消息队列
		    mQueue = mLooper.mQueue;
		    mCallback = callback;
		    mAsynchronous = async;
		}
		.
		.
		.
		/** sendMessage() */
		public final boolean sendMessage(Message msg) {
			return sendMessageDelayed(msg, 0);
		}
		.
		public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
		    Message msg = Message.obtain();
		    msg.what = what;
			return sendMessageDelayed(msg, delayMillis);
		}
		.
		public final boolean sendMessageDelayed(Message msg, long delayMillis) {
			if (delayMillis < 0) {
				delayMillis = 0;
			}
			return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
		}
		...
		// 最后均调用sendMessageAtTime()方法
		// 方法内获取MessageQueue后调用enqueueMessage()方法
		public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
			MessageQueue queue = mQueue;
			if (queue == null) {
			    RuntimeException e = new RuntimeException(
		                   this + " sendMessageAtTime() called with no mQueue");
		           Log.w("Looper", e.getMessage(), e);
		           return false;
		       }
		       return enqueueMessage(queue, msg, uptimeMillis);
		}
		.
		.
		.
		private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
			msg.target = this; // 将当前handler作为target属性
		    if (mAsynchronous) {
			    msg.setAsynchronous(true);
		    }
		    // 将handler发送的消息最终保存到消息队列中
			return queue.enqueueMessage(msg, uptimeMillis);
		}
		.
		.
		.
		/**
		 * 上面说道Looper会不断轮询Handler发送到MessageQueue中的消息,然后回调发送该消息的handler中的dispatchMessage方法,下面请看
		 */
		public void dispatchMessage(Message msg) {
		    if (msg.callback != null) {
		        handleCallback(msg);
		    } else {
		        if (mCallback != null) {
		            if (mCallback.handleMessage(msg)) {
		                return;
		            }
		        }
		        // 该方法为空,即我们自己复写的handleMessage方法,然后根据msg.what进行消息处理
		        handleMessage(msg);
		    }
		}
		```

- **PART-C_小结**

1. Looper.prepare()在本线程中保存一个Looper实例,且该实例中仅保存一个MessageQueue对象.
2. Looper.loop()会让当前线程无限轮询MessageQueue中的消息,然后回调msg.target.dispatchMessage(msg)方法.
3.  Hanlder的构造方法中,先拿到当前线程中保存的Looper实例,然后与实例中的MessageQueue关联.
4. Hanlder的sendMessage方法,会给msg的target赋值为handler自身并加入到MessageQueue中.
5. 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法.

redpajama_set_name: RedPajamaGithub


text:
package com.koushikdutta.ion;

import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.animation.Animation;
import android.widget.ImageView;

import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.async.future.SimpleFuture;
import com.koushikdutta.async.util.FileCache;
import com.koushikdutta.ion.bitmap.BitmapInfo;

import java.lang.ref.WeakReference;

/**
 * Created by koush on 6/8/13.
 */
class IonDrawable extends Drawable {
    private Paint paint;
    private BitmapInfo info;
    private int placeholderResource;
    private Drawable placeholder;
    private int errorResource;
    private Drawable error;
    private Resources resources;
    private int loadedFrom;
    private IonDrawableCallback callback;
    private boolean disableFadeIn;
    private int resizeWidth;
    private int resizeHeight;
    private Ion ion;

    public IonDrawable cancel() {
        requestCount++;
        return this;
    }

    public IonDrawable ion(Ion ion) {
        this.ion = ion;
        return this;
    }

    public Drawable getCurrentDrawable() {
        if (info == null) {
            if (placeholderResource != 0)
                return resources.getDrawable(placeholderResource);
        }
        if (info != null && info.bitmaps != null)
            return new BitmapDrawable(resources, info.bitmaps[0]);
        if (errorResource != 0)
            return resources.getDrawable(errorResource);
        return null;
    }

    public SimpleFuture<ImageView> getFuture() {
        return callback.imageViewFuture;
    }

    public IonDrawable setDisableFadeIn(boolean disableFadeIn) {
        this.disableFadeIn = disableFadeIn;
        return this;
    }

    public IonDrawable setInAnimation(Animation inAnimation, int inAnimationResource) {
        callback.inAnimation = inAnimation;
        callback.inAnimationResource = inAnimationResource;
        return this;
    }

    // create an internal static class that can act as a callback.
    // dont let it hold strong references to anything.
    static class IonDrawableCallback implements FutureCallback<BitmapInfo> {
        private WeakReference<IonDrawable> ionDrawableRef;
        private ContextReference.ImageViewContextReference imageViewRef;
        private String bitmapKey;
        private SimpleFuture<ImageView> imageViewFuture = new SimpleFuture<ImageView>();
        private Animation inAnimation;
        private int inAnimationResource;
        private int requestId;

        public IonDrawableCallback(IonDrawable drawable, ImageView imageView) {
            ionDrawableRef = new WeakReference<IonDrawable>(drawable);
            imageViewRef = new ContextReference.ImageViewContextReference(imageView);
        }

        @Override
        public void onCompleted(Exception e, BitmapInfo result) {
            assert Thread.currentThread() == Looper.getMainLooper().getThread();
            assert result != null;
            // see if the imageview is still alive and cares about this result
            ImageView imageView = imageViewRef.get();
            if (imageView == null)
                return;

            IonDrawable drawable = ionDrawableRef.get();
            if (drawable == null)
                return;

            if (imageView.getDrawable() != drawable)
                return;

            // see if the ImageView is still waiting for the same request
            if (drawable.requestCount != requestId)
                return;

            imageView.setImageDrawable(null);
            drawable.setBitmap(result, result.loadedFrom);
            imageView.setImageDrawable(drawable);
            IonBitmapRequestBuilder.doAnimation(imageView, inAnimation, inAnimationResource);

            if (null != imageViewRef.isAlive()) {
                imageViewFuture.cancelSilently();
                return;
            }

            imageViewFuture.setComplete(e, imageView);
        }
    }

    int requestCount;
    public void register(Ion ion, String bitmapKey) {
        callback.requestId = ++requestCount;
        String previousKey = callback.bitmapKey;
        if (TextUtils.equals(previousKey, bitmapKey))
            return;
        callback.bitmapKey = bitmapKey;
        ion.bitmapsPending.add(bitmapKey, callback);
        if (previousKey == null)
            return;

        // unregister this drawable from the bitmaps that are
        // pending.

        // if this drawable was the only thing waiting for this bitmap,
        // then the removeItem call will return the TransformBitmap/LoadBitmap instance
        // that was providing the result.
        if (ion.bitmapsPending.removeItem(previousKey, callback)) {
            // find out who owns this thing, to see if it is a candidate for removal
            Object owner = ion.bitmapsPending.tag(previousKey);
            if (owner instanceof TransformBitmap) {
                TransformBitmap info = (TransformBitmap)owner;
                ion.bitmapsPending.remove(info.key);
                // this transform is also backed by a LoadBitmap* or a DeferredLoadBitmap, grab that
                // if it is the only waiter
                if (ion.bitmapsPending.removeItem(info.downloadKey, info))
                    owner = ion.bitmapsPending.tag(info.downloadKey);
            }
            if (owner instanceof DeferredLoadBitmap) {
                DeferredLoadBitmap defer = (DeferredLoadBitmap)owner;
                ion.bitmapsPending.remove(defer.key);
            }
        }

        ion.processDeferred();
    }

    private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;

    public IonDrawable(Resources resources, ImageView imageView) {
        this.resources = resources;
        paint = new Paint(DEFAULT_PAINT_FLAGS);
        callback = new IonDrawableCallback(this, imageView);
    }

    int currentFrame;
    private boolean invalidateScheduled;
    private int textureDim;
    private int maxLevel;
    public IonDrawable setBitmap(BitmapInfo info, int loadedFrom) {
        this.loadedFrom = loadedFrom;
        requestCount++;

        if (this.info == info)
            return this;

        invalidateSelf();

        this.info = info;
        currentFrame = 0;
        invalidateScheduled = false;
        if (info == null) {
            callback.bitmapKey = null;
            return this;
        }

        if (info.decoder != null) {
            // find number of tiles across to fit
            double wlevel = (double)info.originalSize.x / TILE_DIM;
            double hlevel = (double)info.originalSize.y / TILE_DIM;

            // find the level: find how many power of 2 tiles are necessary
            // to fit the entire image. ie, fit it into a square.
            double level = Math.max(wlevel, hlevel);
            level = Math.log(level) / LOG_2;

            maxLevel = (int)Math.ceil(level);

            // now, we know the entire image will fit in a square image of
            // this dimension:
            textureDim = TILE_DIM << maxLevel;
        }

        callback.bitmapKey = info.key;
        return this;
    }

    public IonDrawable setSize(int resizeWidth, int resizeHeight) {
        if (this.resizeWidth == resizeWidth && this.resizeHeight == resizeHeight)
            return this;
        this.resizeWidth = resizeWidth;
        this.resizeHeight = resizeHeight;
        invalidateSelf();
        return this;
    }

    public IonDrawable setError(int resource, Drawable drawable) {
        if ((drawable != null && drawable == this.error) || (resource != 0 && resource == errorResource))
            return this;

        this.errorResource = resource;
        this.error = drawable;
        invalidateSelf();
        return this;
    }

    public IonDrawable setPlaceholder(int resource, Drawable drawable) {
        if ((drawable != null && drawable == this.placeholder) || (resource != 0 && resource == placeholderResource))
            return this;

        this.placeholderResource = resource;
        this.placeholder = drawable;
        invalidateSelf();

        return this;
    }

    @Override
    public void setFilterBitmap(boolean filter) {
        paint.setFilterBitmap(filter);
        invalidateSelf();
    }

    @Override
    public void setDither(boolean dither) {
        paint.setDither(dither);
        invalidateSelf();
    }

    private Drawable tryGetErrorResource() {
        if (error != null)
            return error;
        if (errorResource == 0)
            return null;
        return error = resources.getDrawable(errorResource);
    }

    @Override
    public int getIntrinsicWidth() {
        if (info != null) {
            if (info.decoder != null)
                return info.originalSize.x;
            if (info.bitmaps != null)
                return info.bitmaps[0].getScaledWidth(resources.getDisplayMetrics().densityDpi);
        }
        if (resizeWidth > 0)
            return resizeWidth;
        if (info != null) {
            Drawable error = tryGetErrorResource();
            if (error != null)
                return error.getIntrinsicWidth();
        }
        if (placeholder != null) {
            return placeholder.getIntrinsicWidth();
        } else if (placeholderResource != 0) {
            Drawable d = resources.getDrawable(placeholderResource);
            assert d != null;
            return d.getIntrinsicWidth();
        }
        return -1;
    }

    @Override
    public int getIntrinsicHeight() {
        if (info != null) {
            if (info.decoder != null)
                return info.originalSize.y;
            if (info.bitmaps != null)
                return info.bitmaps[0].getScaledHeight(resources.getDisplayMetrics().densityDpi);
        }
        if (resizeHeight > 0)
            return resizeHeight;
        if (info != null) {
            if (error != null) {
                return error.getIntrinsicHeight();
            } else if (errorResource != 0) {
                Drawable d = resources.getDrawable(errorResource);
                assert d != null;
                return d.getIntrinsicHeight();
            }
        }
        if (placeholder != null) {
            return placeholder.getIntrinsicHeight();
        } else if (placeholderResource != 0) {
            Drawable d = resources.getDrawable(placeholderResource);
            assert d != null;
            return d.getIntrinsicHeight();
        }
        return -1;
    }

    public static final long FADE_DURATION = 200;
    private Runnable invalidate = new Runnable() {
        @Override
        public void run() {
            invalidateScheduled = false;
            currentFrame++;
            invalidateSelf();
        }
    };

    private static final double LOG_2 = Math.log(2);
    private static final int TILE_DIM = 256;

    FutureCallback<BitmapInfo> tileCallback = new FutureCallback<BitmapInfo>() {
        @Override
        public void onCompleted(Exception e, BitmapInfo result) {
            invalidateSelf();
        }
    };

    @Override
    public void draw(Canvas canvas) {
        if (info == null) {
            if (placeholder == null && placeholderResource != 0)
                placeholder = resources.getDrawable(placeholderResource);
            if (placeholder != null) {
                placeholder.setBounds(getBounds());
                placeholder.draw(canvas);
            }
            return;
        }

        if (info.drawTime == 0)
            info.drawTime = SystemClock.uptimeMillis();

        long destAlpha = 0xFF;

        if(!disableFadeIn) {
            destAlpha = ((SystemClock.uptimeMillis() - info.drawTime) << 8) / FADE_DURATION;
            destAlpha = Math.min(destAlpha, 0xFF);
        }

        if (destAlpha != 255) {
            if (placeholder == null && placeholderResource != 0)
                placeholder = resources.getDrawable(placeholderResource);
            if (placeholder != null) {
                placeholder.setBounds(getBounds());
                placeholder.draw(canvas);
            }
        }

        if (info.decoder != null) {
            // zoom 0: entire image fits in a TILE_DIMxTILE_DIM square

            // draw base bitmap for empty tiles
            // figure out zoom level
            // figure out which tiles need rendering
            // draw stuff that needs drawing
            // missing tile? fetch it
            // use parent level tiles for tiles that do not exist

            // TODO: crossfading?

            Rect clip = canvas.getClipBounds();
            Rect bounds = getBounds();

            float zoom = (float)canvas.getWidth() / (float)clip.width();

            float zoomWidth = zoom * bounds.width();
            float zoomHeight = zoom * bounds.height();

            double wlevel = Math.log(zoomWidth / TILE_DIM) / LOG_2;
            double hlevel = Math.log(zoomHeight/ TILE_DIM) / LOG_2;
            double maxLevel = Math.max(wlevel, hlevel);

            int visibleLeft = Math.max(0, clip.left);
            int visibleRight = Math.min(bounds.width(), clip.right);
            int visibleTop = Math.max(0, clip.top);
            int visibleBottom = Math.min(bounds.height(), clip.bottom);
            int level = (int)Math.floor(maxLevel);
            level = Math.min(this.maxLevel, level);
            level = Math.max(level, 0);
            int levelTiles = 1 << level;
            int textureTileDim = textureDim / levelTiles;
//            System.out.println("textureTileDim: " + textureTileDim);

//            System.out.println(info.key + " visible: " + new Rect(visibleLeft, visibleTop, visibleRight, visibleBottom));

            final boolean DEBUG_ZOOM = false;
            if (info.bitmaps != null && info.bitmaps[0] != null) {
                canvas.drawBitmap(info.bitmaps[0], null, getBounds(), paint);
                if (DEBUG_ZOOM) {
                    paint.setColor(Color.RED);
                    paint.setAlpha(0x80);
                    canvas.drawRect(getBounds(), paint);
                    paint.setAlpha(0xFF);
                }
            }
            else {
                paint.setColor(Color.BLACK);
                canvas.drawRect(getBounds(), paint);
            }

            int sampleSize = 1;
            while (textureTileDim / sampleSize > TILE_DIM)
                sampleSize <<= 1;

            for (int y = 0; y < levelTiles; y++) {
                int top = textureTileDim * y;
                int bottom = textureTileDim * (y + 1);
                bottom = Math.min(bottom, bounds.bottom);
                // TODO: start at visible pos
                if (bottom < visibleTop)
                    continue;
                if (top > visibleBottom)
                    break;
                for (int x = 0; x < levelTiles; x++) {
                    int left = textureTileDim * x;
                    int right = textureTileDim * (x + 1);
                    right = Math.min(right, bounds.right);
                    // TODO: start at visible pos
                    if (right < visibleLeft)
                        continue;
                    if (left > visibleRight)
                        break;

                    Rect texRect = new Rect(left, top, right, bottom);

                    // find, render/fetch
//                    System.out.println("rendering: " + texRect + " for: " + bounds);
                    String tileKey = FileCache.toKeyString(info.key, ",", level, ",", x, ",", y);
                    BitmapInfo tile = ion.bitmapCache.get(tileKey);
                    if (tile != null && tile.bitmaps != null) {
                        // render it
//                        System.out.println("bitmap is: " + tile.bitmaps[0].getWidth() + "x" + tile.bitmaps[0].getHeight());
                        canvas.drawBitmap(tile.bitmaps[0], null, texRect, paint);
                        continue;
                    }

                    // TODO: cancellation of unnecessary regions when fast pan/zooming
                    if (ion.bitmapsPending.tag(tileKey) == null) {
                        // fetch it
//                        System.out.println(info.key + ": fetching region: " + texRect + " sample size: " + sampleSize);
                        LoadBitmapRegion region = new LoadBitmapRegion(ion, tileKey, info.decoder, texRect, sampleSize);
                    }
                    ion.bitmapsPending.add(tileKey, tileCallback);

                    int parentLeft = 0;
                    int parentTop = 0;
                    int parentUp = 1;
                    int parentLevel = level - parentUp;
                    if (x % 2 == 1)
                        parentLeft++;
                    if (y % 2 == 1)
                        parentTop++;
                    int parentX = x >> 1;
                    int parentY = y >> 1;

                    while (parentLevel >= 0) {
                        tileKey = FileCache.toKeyString(info.key, ",", parentLevel, ",", parentX, ",", parentY);
                        tile = ion.bitmapCache.get(tileKey);
                        if (tile != null && tile.bitmaps != null)
                            break;
                        if (parentX % 2 == 1) {
                            parentLeft += 1 << parentUp;
                        }
                        if (parentY % 2 == 1) {
                            parentTop += 1 << parentUp;
                        }
                        parentLevel--;
                        parentUp++;
                        parentX >>= 1;
                        parentY >>= 1;
                    }

                    // well, i give up
                    if (tile == null || tile.bitmaps == null)
                        continue;

                    int subLevelTiles = 1 << parentLevel;
                    int subtileDim = textureDim / subLevelTiles;
                    int subSampleSize = 1;
                    while (subtileDim / subSampleSize > TILE_DIM)
                        subSampleSize <<= 1;
                    int subTextureDim = subtileDim / subSampleSize;
//                    System.out.println(String.format("falling back for %s,%s,%s to %s,%s,%s: %s,%s (%s to %s)", x, y, level, parentX, parentY, parentLevel, parentLeft, parentTop, subTextureDim, subTextureDim >> parentUp));
                    subTextureDim >>= parentUp;
                    int sourceLeft = subTextureDim * parentLeft;
                    int sourceTop = subTextureDim * parentTop;
                    Rect sourceRect = new Rect(sourceLeft, sourceTop, sourceLeft + subTextureDim, sourceTop + subTextureDim);
                    canvas.drawBitmap(tile.bitmaps[0], sourceRect, texRect, paint);

                    if (DEBUG_ZOOM) {
                        paint.setColor(Color.RED);
                        paint.setAlpha(0x80);
                        canvas.drawRect(texRect, paint);
                        paint.setAlpha(0xFF);
                    }
                }
            }
        }
        else if (info.bitmaps != null) {
            paint.setAlpha((int)destAlpha);
            canvas.drawBitmap(info.bitmaps[currentFrame % info.bitmaps.length], null, getBounds(), paint);
            paint.setAlpha(0xFF);
            if (info.delays != null) {
                int delay = info.delays[currentFrame % info.delays.length];
                if (!invalidateScheduled) {
                    invalidateScheduled = true;
                    unscheduleSelf(invalidate);
                    scheduleSelf(invalidate, SystemClock.uptimeMillis() + Math.max(delay, 100));
                }
            }
        }
        else {
            Drawable error = tryGetErrorResource();
            if (error != null) {
                error.setAlpha((int)destAlpha);
                error.setBounds(getBounds());
                error.draw(canvas);
                error.setAlpha(0xFF);
            }
        }

        if (destAlpha != 255)
            invalidateSelf();

        if (true)
            return;

        // stolen from picasso
        canvas.save();
        canvas.rotate(45);

        paint.setColor(Color.WHITE);
        canvas.drawRect(0, -10, 7.5f, 10, paint);

        int sourceColor;
        switch (loadedFrom) {
            case Loader.LoaderEmitter.LOADED_FROM_CACHE:
                sourceColor = Color.CYAN;
                break;
            case Loader.LoaderEmitter.LOADED_FROM_CONDITIONAL_CACHE:
                sourceColor = Color.YELLOW;
                break;
            case Loader.LoaderEmitter.LOADED_FROM_MEMORY:
                sourceColor = Color.GREEN;
                break;
            default:
                sourceColor = Color.RED;
                break;
        }

        paint.setColor(sourceColor);
        canvas.drawRect(0, -9, 6.5f, 9, paint);

        canvas.restore();
    }

    @Override
    public void setAlpha(int alpha) {
       paint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        paint.setColorFilter(cf);
    }

    @Override
    public int getOpacity() {
        return (info == null || info.bitmaps == null || info.bitmaps[0].hasAlpha() || paint.getAlpha() < 255) ?
                PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
    }

    static IonDrawable getOrCreateIonDrawable(ImageView imageView) {
        Drawable current = imageView.getDrawable();
        IonDrawable ret;
        if (current == null || !(current instanceof IonDrawable))
            ret = new IonDrawable(imageView.getResources(), imageView);
        else
            ret = (IonDrawable)current;
        // invalidate self doesn't seem to trigger the dimension check to be called by imageview.
        // are drawable dimensions supposed to be immutable?
        imageView.setImageDrawable(null);
        return ret;
    }
}


text:
category: "page/manager"
title: "ClientPageManager"
---

## ClientPageManager
Page manager for controller on the client side.

**Kind**: global class

* [ClientPageManager](#ClientPageManager)
    * [new ClientPageManager(pageFactory, pageRenderer, stateManager, handlerRegistry, window, eventBus)](#new_ClientPageManager_new)
    * [._window](#ClientPageManager+_window) : ima.window.Window
    * [._eventBus](#ClientPageManager+_eventBus) : ima.event.EventBus
    * [._boundOnCustomEventHandler()](#ClientPageManager+_boundOnCustomEventHandler) : function
    * [.init()](#ClientPageManager+init)
    * [.manage()](#ClientPageManager+manage)
    * [.destroy()](#ClientPageManager+destroy)
    * [._onCustomEventHandler(event)](#ClientPageManager+_onCustomEventHandler)
    * [._parseCustomEvent(event)](#ClientPageManager+_parseCustomEvent) ⇒ Object
    * [._handleEventWithController(method, data)](#ClientPageManager+_handleEventWithController) ⇒ boolean
    * [._handleEventWithExtensions(method, data)](#ClientPageManager+_handleEventWithExtensions) ⇒ boolean

* * *

### new ClientPageManager(pageFactory, pageRenderer, stateManager, handlerRegistry, window, eventBus)
Initializes the client-side page manager.

| Param | Type | Description |
| --- | --- | --- |
| pageFactory | PageFactory | Factory used by the page manager to        create instances of the controller for the current route, and        decorate the controllers and page state managers. |
| pageRenderer | PageRenderer | The current renderer of the page. |
| stateManager | PageStateManager | The current page state manager. |
| handlerRegistry | HandlerRegistry | Instance of HandlerRegistry that        holds a list of pre-manage and post-manage handlers. |
| window | Window | The utility for manipulating the global context        and global client-side-specific APIs. |
| eventBus | EventBus | The event bus for dispatching and listening        for custom IMA events propagated through the DOM. |

* * *

### clientPageManager.\_window : ima.window.Window
The utility for manipulating the global context and global
client-side-specific APIs.

**Kind**: instance property of [ClientPageManager](#ClientPageManager)

* * *

### clientPageManager.\_eventBus : ima.event.EventBus
The event bus for dispatching and listening for custom IMA events
propagated through the DOM.

**Kind**: instance property of [ClientPageManager](#ClientPageManager)

* * *

### clientPageManager.\_boundOnCustomEventHandler() : function
Event listener for the custom DOM events used by the event bus,
bound to this instance.

**Kind**: instance method of [ClientPageManager](#ClientPageManager)

* * *

### clientPageManager.init()
**Kind**: instance method of [ClientPageManager](#ClientPageManager)

* * *

### clientPageManager.manage()
**Kind**: instance method of [ClientPageManager](#ClientPageManager)

* * *

### clientPageManager.destroy()
**Kind**: instance method of [ClientPageManager](#ClientPageManager)

* * *

### clientPageManager.\_onCustomEventHandler(event)
Custom DOM event handler.

The handler invokes the event listener in the active controller, if such
listener is present. The name of the controller's listener method is
created by turning the first symbol of the event's name to upper case,
and then prefixing the result with the 'on' prefix.

For example: for an event named 'toggle' the controller's listener
would be named 'onToggle'.

The controller's listener will be invoked with the event's data as an
argument.

**Kind**: instance method of [ClientPageManager](#ClientPageManager)

| Param | Type | Description |
| --- | --- | --- |
| event | CustomEvent | The encountered event bus DOM event. |

* * *

### clientPageManager.\_parseCustomEvent(event) ⇒ Object
Extracts the details of the provided event bus custom DOM event, along
with the expected name of the current controller's method for
intercepting the event.

**Kind**: instance method of [ClientPageManager](#ClientPageManager)
**Returns**: Object - The event's
        details.

| Param | Type | Description |
| --- | --- | --- |
| event | CustomEvent | The encountered event bus custom DOM event. |

* * *

### clientPageManager.\_handleEventWithController(method, data) ⇒ boolean
Attempts to handle the currently processed event bus custom DOM event
using the current controller. The method returns true if the
event is handled by the controller.

**Kind**: instance method of [ClientPageManager](#ClientPageManager)
**Returns**: boolean - true if the event has been handled by the
        controller, false if the controller does not have a
        method for processing the event.

| Param | Type | Description |
| --- | --- | --- |
| method | string | The name of the method the current controller        should use to process the currently processed event bus custom        DOM event. |
| data | Object.<string, \*> | The custom event's data. |

* * *

### clientPageManager.\_handleEventWithExtensions(method, data) ⇒ boolean
Attempts to handle the currently processed event bus custom DOM event
using the registered extensions of the current controller. The method
returns true if the event is handled by the controller.

**Kind**: instance method of [ClientPageManager](#ClientPageManager)
**Returns**: boolean - true if the event has been handled by one of
        the controller's extensions, false if none of the
        controller's extensions has a method for processing the event.

| Param | Type | Description |
| --- | --- | --- |
| method | string | The name of the method the current controller        should use to process the currently processed event bus custom        DOM event. |
| data | Object.<string, \*> | The custom event's data. |

* * *


text:
const fs = require('fs');
const path = require('path');

const isBrowser = window.isBrowser;
const isLocal = !isBrowser;

export class Filemanager {
	/**
	 *
	 * @param {import('./ccloader').ModLoader} modloader
	 */
	constructor(modloader) {
		this.modloader = modloader;
		this.packed = [];
		// eslint-disable-next-line no-undef
		this._packedManager = new PackedManager();

		if (isBrowser) {
			this.getResourceAsync('../mods.json').then((text) => {
				this.modList = JSON.parse(text);
			}).catch((e) => {
				console.error('Could not load mod list. Proceeding to load without any mods. ', e);
				this.modList = [];
			});
		}
	}

	/**
	 *
	 * @param {string[]} names
	 */
	setPackedMods(names) {
		this.packed = names;
	}

	/**
	 *
	 * @param {string} file
	 * @param {boolean} isModule
	 */
	loadMod(file, isModule) {
		return this._loadScript(file, document, isModule ? 'module' : 'text/javascript');
	}

	/**
	 *
	 * @param {string} folder
	 */
	getAllModsFiles(folder) {
		const subs = this._getFolders(folder);
		return [].concat(...subs.map(sub => this._getResourcesInFolder(sub, path.sep + 'package.json')));
	}

	/**
	 *
	 * @param {string} folder
	 */
	getAllCCModFiles(folder) {
		const subs = this._getFolders(folder);
		return [].concat(...subs.map(sub => this._getResourcesInFolder(sub, path.sep + 'ccmod.json')));
	}

	/**
	 *
	 * @param {string} folder
	 */
	getAllModPackages(folder) {
		return this._getResourcesInFolder(folder, '.ccmod');
	}

	/**
	 *
	 * @param {string} resource
	 * @returns {Promise<string>}
	 */
	async getResourceAsync(resource) {
		resource = resource.replace(/\\/g, '/');
		if (resource.startsWith('assets/') || resource.startsWith('ccloader/')) {
			resource = '/' + resource;
		}

		const resp = await fetch(resource);
		return await resp.text();
	}

	/**
	 *
	 * @param {string} dir
	 * @param {string[]} [endings]
	 * @returns {Promise<string[]>}
	 */
	async findFiles(dir, endings) {
		try {
			const files = await this._getFiles(dir);
			if (files.length === 0) {
				return [];
			}

			const promises = [];
			for (const file of files){
				promises.push(this._checkFileForAsset(dir, file, endings));
			}
			const results = await Promise.all(promises);

			return [].concat(...results); //Flattens the arrays
		} catch (e) {
			return [];
		}
	}

	/**
	 *
	 * @param {string} file
	 * @returns {Promise<boolean>}
	 */
	async packedFileExists(file) {
		if (!this.isPacked(file)) {
			return false;
		}

		return (await fetch(file, {
			headers: {
				'X-Cmd': 'isFile'
			}
		})).json();
	}

	/**
	 *
	 * @param {string} path
	 * @returns {Promise<Image>}
	 */
	loadImage(path) {
		return new Promise((resolve, reject) => {
			const result = new Image();
			result.onload = () => resolve(result);
			result.onerror = err => reject(err);
			result.src = path;
		});
	}

	/**
	 *
	 * @param {string} path
	 * @param {window} window
	 * @returns {Promise<ServiceWorker>}
	 */
	async loadServiceWorker(path, window) {
		const currentRegistration = await window.navigator.serviceWorker.getRegistration();
		if (currentRegistration) {
			//Do not await update since the worker only performs a simple task. Even if there is a bugfix it should be enough to not crash.
			currentRegistration.update();
		} else {
			await window.navigator.serviceWorker.register(path, { updateViaCache: 'none' });
		}

		if (!window.navigator.serviceWorker.controller || window.navigator.serviceWorker.controller.state !== 'activated') {
			window.location.reload();
			window.location.href = window.location.toString();
			history.go(0);

			throw new Error('(╯°□°)╯︵ ┻━┻');
		}

		return window.navigator.serviceWorker.controller;
	}

	/**
	 *
	 * @param {string} path
	 */
	isPacked(path) {
		return this.packed.includes(this._packedManager.packedName(path));
	}

	/**
	 * Returns an array of the extensions in 'assets/extensions'
	 */
	getExtensions() {
		return this._getFolders('assets/extension').map(value => value.substr(17))
	}

	/**
	 * Returns all files with the given ending in the folder
	 * @param {string} folder
	 */
	_getFolders(folder) {
		if (!folder)
			folder = 'assets/mods/';

		if (isLocal) {
			return this._getLocalFolders(folder);
		} else {
			var results = [];
			for (var i in this.modList) {
				if (this._resourceExists(folder + this.modList[i])) {
					results.push(folder + this.modList[i]);
				}
			}
			return results;
		}
	}

	/**
	 * Returns all files with the given ending in the folder
	 * @param {string?} folder
	 * @param {string?} ending
	 */
	_getResourcesInFolder(folder, ending) {
		if (!folder)
			folder = 'assets/mods/';

		if (isLocal) {
			return this._getResoucesInLocalFolder(folder, ending);
		} else if (folder.endsWith('mods/')) {
			var results = [];
			for (var i in this.modList) {
				if (this._resourceExists(folder + this.modList[i] + ending)) {
					results.push(folder + this.modList[i] + ending);
				}
			}
			return results;
		} else {
			if (this._resourceExists(folder + '/' + ending))
				return [folder + '/' + ending];
			return [];
		}
	}

	/**
	 *
	 * @param {string} dir
	 * @returns {Promise<string[]>}
	 */
	async _getFiles(dir) {
		if (this.isPacked(dir)) {
			return (await fetch(dir, {
				headers: {
					'X-Cmd': 'getFiles'
				}
			})).json();
		}

		return new Promise((resolve, reject) => {
			fs.readdir(dir, (err, files) => {
				if (err) {
					reject(err);
				} else {
					resolve(files);
				}
			});
		});
	}

	/**
	 *
	 * @param {string} file
	 * @returns {Promise<fs.Stats>}
	 */
	_getStats(file) {
		return new Promise((resolve, reject) => {
			fs.stat(file, (err, stats) => {
				if (err) {
					reject(err);
				} else {
					resolve(stats);
				}
			});
		});
	}

	/**
	 *
	 * @param {string} file
	 * @returns {Promise<boolean>}
	 */
	async _isDirectoryAsync(file) {
		if (this.isPacked(file)) {
			return (await fetch(file, {
				headers: {
					'X-Cmd': 'isDirectory'
				}
			})).json();
		}

		const stats = await this._getStats(file);
		return stats && stats.isDirectory();
	}

	/**
	 *
	 * @param {string} dir
	 * @param {string} file
	 * @param {string[]} [endings]
	 * @returns {Promise<string[]>}
	 */
	async _checkFileForAsset(dir, file, endings) {
		const filePath = path.join(dir, file);

		try {
			if (await this._isDirectoryAsync(filePath)) {
				return await this.findFiles(filePath, endings);
			} else if (!endings || endings.some(ending => filePath.endsWith(ending))) {
				return [this.filePathToAssetPath(filePath)];
			}
			return [];
		} catch (e) {
			return [];
		}
	}

	/**
	 *
	 * @param {string} filePath
	 * @returns {string}
	 */
	filePathToAssetPath(filePath) {
		return filePath.substr(7).replace(/\\/g, '/');
	}

	/**
	 *
	 * @param {string} resource
	 */
	_resourceExists(resource) {
		if (isLocal) {
			try {
				fs.statSync(resource);
				return true;
			} catch (e) {
				return false;
			}
		} else {
			try {
				const req = new XMLHttpRequest();
				req.open('HEAD', '/' + resource, false);
				req.send();
				return req.status != 404;
			} catch (e) {
				return false;
			}
		}
	}

	/**
	 *
	 * @param {string} file
	 * @returns {Promise<Blob>}
	 */
	async _getBlob(file) {
		const resp = await fetch(file);
		return resp.blob();
	}

	/**
	 *
	 * @param {string} url
	 * @param {document} doc
	 * @param {string} type
	 * @returns {Promise<void>}
	 */
	_loadScript(url, doc, type) {
		if (!type) {
			type = 'text/javascript';
		}

		return new Promise((resolve, reject) => {
			const script = document.createElement('script');
			script.onload = () => resolve();
			script.onerror = () => reject();
			script.type = type;
			script.src = url;
			doc.head.appendChild(script);
		});
	}

	/**
	 * Returns all files with the given ending in the folder
	 * @param {string} folder
	 * @param {string?} ending
	 */
	_getResoucesInLocalFolder(folder, ending) {
		/** @type {string[]} */
		let results = [];

		if (isLocal) {
			try {
				fs.readdirSync(folder).forEach(file => {
					try {
						file = path.join(folder, file);

						if (!this._isDirectory(file) && file.endsWith(ending)) {
							results.push(file);
						}
					} catch (e) { }
				});
			} catch (e) { }
		}

		return results;
	}

	/**
	 * Returns all files with the given ending in the folder
	 * @param {string} folder
	 */
	_getLocalFolders(folder) {
		/** @type {string[]} */
		let results = [];

		if (isLocal) {
			try {
				return fs.readdirSync(folder)
					.map(file => path.join(folder, file))
					.filter(file => this._isDirectory(file));
			} catch (e) { }
		}

		return results;
	}

	/**
	 *
	 * @param {string} file
	 * @returns {boolean}
	 */
	_isDirectory(file) {
		const stat = fs.statSync(file);
		return stat && stat.isDirectory();
	}

	_createDirectories() {
		if (isLocal) {
			this._createDirectory('ccloader/data/assets/mods');
		}
	}

	/**
	 *
	 * @param {string} dir
	 */
	_createDirectory(dir) {
		if (isBrowser) {
			return;
		}

		if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
			return;
		}

		const parent = path.join(dir, '..');
		this._createDirectory(parent);

		fs.mkdirSync(dir);
	}
}


text:
require 'test_helper'

class UsersControllerTest < ActionController::TestCase
  def setup
    @admin = User.create(first_name: "Example", last_name: "User", email: "user@example.com", password: "testpassword", admin: true)
    @user = User.create(first_name: "Example", last_name: "User", email: "user@example.com", password: "testpassword")
  end

  test "should get new" do
    get :new
    assert_response :success
  end

  test "should get index" do
    sign_in @admin
    get :index
    assert_response :success
  end

  test "should get show" do
    sign_in @user
    get :show
    assert_response :success
  end

end


text:
package org.apache.tinkerpop.gremlin.jsr223.console;

/**
 * Provides an abstraction over a "Gremlin Shell" (i.e. the core of a console), enabling the plugin to not have to
 * be hardcoded specifically to any particular shell, like the Gremlin Groovy Console, and thus allowing it to
 * not have to depend on the gremlin-groovy module itself.
 *
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public interface GremlinShellEnvironment {

    public <T> T getVariable(final String variableName);

    public <T> void setVariable(final String variableName, final T variableValue);

    public void println(final String line);

    public default void errPrintln(final String line) {
        println(line);
    }

    public <T> T execute(final String line);
}


text:
package com.facebook.buck.distributed.build_slave;

import com.facebook.buck.distributed.build_slave.DistBuildTrace.MinionThread;
import com.facebook.buck.distributed.build_slave.DistBuildTrace.MinionTrace;
import com.facebook.buck.distributed.build_slave.DistBuildTrace.RuleTrace;
import com.facebook.buck.event.chrome_trace.ChromeTraceEvent;
import com.facebook.buck.event.chrome_trace.ChromeTraceEvent.Phase;
import com.facebook.buck.event.chrome_trace.ChromeTraceWriter;
import com.facebook.buck.log.Logger;
import com.facebook.buck.util.network.hostname.HostnameFetching;
import com.google.common.collect.ImmutableMap;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Optional;
import java.util.Stack;

/** Chrome trace generator for {@link DistBuildTrace} object. */
public class DistBuildChromeTraceRenderer {
  private static final Logger LOG = Logger.get(DistBuildChromeTraceRenderer.class);

  private static final String kTraceCategory = "buck";
  private static final String kNewSectionKey = "process_name";
  private static final String kNewSectionNameLabel = "name";

  private final DistBuildTrace trace;
  private final ChromeTraceWriter chromeTraceWriter;
  private int pidForTrace = 0;

  private DistBuildChromeTraceRenderer(DistBuildTrace trace, OutputStream outputStream)
      throws IOException {
    this.trace = trace;
    this.chromeTraceWriter = new ChromeTraceWriter(outputStream);
  }

  private void writeTraceFile() throws IOException {
    LOG.info("Starting to write Stampede ChromeTrace.");
    chromeTraceWriter.writeStart();
    // This metadata is not visible in Chrome trace display
    // but can be extracted with tools like jq
    chromeTraceWriter.writeEvent(
        new ChromeTraceEvent(
            kTraceCategory,
            "global_metadata",
            Phase.METADATA,
            0,
            0,
            0,
            0,
            ImmutableMap.of(
                "stampedeId", trace.stampedeId,
                "hostname", HostnameFetching.getHostname(),
                "timestamp", Instant.now().toString())));
    renderCriticalPath();
    renderMinions();
    chromeTraceWriter.writeEnd();
    chromeTraceWriter.close();
    LOG.info("Finished writing Stampede ChromeTrace.");
  }

  private void renderCriticalPath() throws IOException {
    Optional<RuleTrace> optionalRule = trace.criticalNode;
    if (!optionalRule.isPresent()) {
      return;
    }

    pidForTrace++;
    chromeTraceWriter.writeEvent(
        new ChromeTraceEvent(
            kTraceCategory,
            kNewSectionKey,
            Phase.METADATA,
            pidForTrace,
            0,
            0,
            0,
            ImmutableMap.of(kNewSectionNameLabel, "Critical Path")));

    Stack<RuleTrace> criticalPath = new Stack<>();
    do {
      criticalPath.push(optionalRule.get());
      optionalRule = optionalRule.get().previousRuleInLongestDependencyChain;
    } while (optionalRule.isPresent());

    long lastEventFinishMicros = 0;
    while (!criticalPath.empty()) {
      RuleTrace ruleTrace = criticalPath.pop();
      lastEventFinishMicros = renderRule(ruleTrace, 0, lastEventFinishMicros);
    }
  }

  private void renderMinions() throws IOException {
    for (int minionIndex = 0; minionIndex < trace.minions.size(); minionIndex++) {
      MinionTrace minionTrace = trace.minions.get(minionIndex);

      pidForTrace++;
      chromeTraceWriter.writeEvent(
          new ChromeTraceEvent(
              kTraceCategory,
              kNewSectionKey,
              Phase.METADATA,
              pidForTrace,
              0,
              0,
              0,
              ImmutableMap.of(kNewSectionNameLabel, minionTrace.minionId)));

      for (int threadId = 0; threadId < minionTrace.threads.size(); threadId++) {
        MinionThread line = minionTrace.threads.get(threadId);

        long lastEventFinishMicros = 0;
        for (RuleTrace ruleEntry : line.ruleTraces) {
          lastEventFinishMicros = renderRule(ruleEntry, threadId, lastEventFinishMicros);
        }
      }
    }
  }

  private long renderRule(RuleTrace ruleEntry, int threadId, long lastEventFinishMicros)
      throws IOException {
    // Work around Chrome trace renderer: it renders new lines
    // for zero width events connected to previous events.
    // So we are adjusting events so that:
    // * each event is at least 1 microsecond
    // * adjusted events do not overlap
    // For example, this sequence event would produce two lines:
    // | 5ms event | 0ms event | 5ms event |
    // So we render these events like this:
    // | 5ms event | 1us event | 4ms499us event |

    long startMicros = Math.max(ruleEntry.startEpochMillis * 1000, lastEventFinishMicros);

    lastEventFinishMicros = Math.max(ruleEntry.finishEpochMillis * 1000, startMicros + 1);

    ImmutableMap.Builder<String, String> startArgs = ImmutableMap.builder();

    startArgs.put(
        "critical_blocking_rule",
        ruleEntry.previousRuleInLongestDependencyChain.map(rule -> rule.ruleName).orElse("None"));
    startArgs.put(
        "length_of_critical_path", String.format("%dms", ruleEntry.longestDependencyChainMillis));
    startArgs.put("number_of_dependents", String.format("%d", ruleEntry.numberOfDependents));

    // This formatting helps in searching for the rule in the Chrome Trace.
    // Otherwise the same name might appear in critical dependencies of other rules.
    String ruleTitle = String.format("%s - title", ruleEntry.ruleName);
    chromeTraceWriter.writeEvent(
        new ChromeTraceEvent(
            kTraceCategory,
            ruleTitle,
            Phase.BEGIN,
            pidForTrace,
            threadId,
            startMicros,
            0,
            startArgs.build()));
    chromeTraceWriter.writeEvent(
        new ChromeTraceEvent(
            kTraceCategory,
            ruleTitle,
            Phase.END,
            pidForTrace,
            threadId,
            lastEventFinishMicros,
            0,
            ImmutableMap.of()));

    return lastEventFinishMicros;
  }

  /** Generate an HTML Chrome-trace based on the given {@link DistBuildTrace} object. */
  public static void render(DistBuildTrace trace, Path path) throws IOException {
    try (FileOutputStream outputStream = new FileOutputStream(path.toFile())) {
      DistBuildChromeTraceRenderer renderer = new DistBuildChromeTraceRenderer(trace, outputStream);
      renderer.writeTraceFile();
    }
  }
}


text:
<?php

namespace FluentDOM {

  /**
   * FluentDOM\Attribute extends PHPs DOMAttr class.
   *
   * @property Document $ownerDocument
   */
  class Attribute
    extends \DOMAttr  {

    use Node\Xpath;

    /**
     * Casting the element node to string will returns its value
     *
     * @return string
     */
    public function __toString() {
      return $this->value;
    }
  }
}


text:
module RegAlloc.Linear.SPARC.FreeRegs
where

import GhcPrelude

import SPARC.Regs
import RegClass
import Reg

import CodeGen.Platform
import Outputable
import Platform

import Data.Word
import Data.Bits
import Data.Foldable (foldl')

--------------------------------------------------------------------------------
-- SPARC is like PPC, except for twinning of floating point regs.
--      When we allocate a double reg we must take an even numbered
--      float reg, as well as the one after it.

-- Holds bitmaps showing what registers are currently allocated.
--      The float and double reg bitmaps overlap, but we only alloc
--      float regs into the float map, and double regs into the double map.
--
--      Free regs have a bit set in the corresponding bitmap.
--
data FreeRegs
        = FreeRegs
                !Word32         -- int    reg bitmap    regs  0..31
                !Word32         -- float  reg bitmap    regs 32..63
                !Word32         -- double reg bitmap    regs 32..63

instance Show FreeRegs where
        show = showFreeRegs

-- | A reg map where no regs are free to be allocated.
noFreeRegs :: FreeRegs
noFreeRegs = FreeRegs 0 0 0

-- | The initial set of free regs.
initFreeRegs :: Platform -> FreeRegs
initFreeRegs platform
 =      foldl' (flip $ releaseReg platform) noFreeRegs allocatableRegs

-- | Get all the free registers of this class.
getFreeRegs :: RegClass -> FreeRegs -> [RealReg]        -- lazily
getFreeRegs cls (FreeRegs g f d)
        | RcInteger <- cls = map RealRegSingle                  $ go 1 g 1 0
        | RcFloat   <- cls = map RealRegSingle                  $ go 1 f 1 32
        | RcDouble  <- cls = map (\i -> RealRegPair i (i+1))    $ go 2 d 1 32
        | otherwise = pprPanic "RegAllocLinear.getFreeRegs: Bad register class " (ppr cls)
        where
                go _    _      0    _
                        = []

                go step bitmap mask ix
                        | bitmap .&. mask /= 0
                        = ix : (go step bitmap (mask `shiftL` step) $! ix + step)

                        | otherwise
                        = go step bitmap (mask `shiftL` step) $! ix + step

-- | Grab a register.
allocateReg :: Platform -> RealReg -> FreeRegs -> FreeRegs
allocateReg platform
         reg@(RealRegSingle r)
             (FreeRegs g f d)

        -- can't allocate free regs
        | not $ freeReg platform r
        = pprPanic "SPARC.FreeRegs.allocateReg: not allocating pinned reg" (ppr reg)

        -- a general purpose reg
        | r <= 31
        = let   mask    = complement (bitMask r)
          in    FreeRegs
                        (g .&. mask)
                        f
                        d

        -- a float reg
        | r >= 32, r <= 63
        = let   mask    = complement (bitMask (r - 32))

                -- the mask of the double this FP reg aliases
                maskLow = if r `mod` 2 == 0
                                then complement (bitMask (r - 32))
                                else complement (bitMask (r - 32 - 1))
          in    FreeRegs
                        g
                        (f .&. mask)
                        (d .&. maskLow)

        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not allocating bad reg" (ppr reg)

allocateReg _
         reg@(RealRegPair r1 r2)
             (FreeRegs g f d)

        | r1 >= 32, r1 <= 63, r1 `mod` 2 == 0
        , r2 >= 32, r2 <= 63
        = let   mask1   = complement (bitMask (r1 - 32))
                mask2   = complement (bitMask (r2 - 32))
          in
                FreeRegs
                        g
                        ((f .&. mask1) .&. mask2)
                        (d .&. mask1)

        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not allocating bad reg" (ppr reg)

-- | Release a register from allocation.
--      The register liveness information says that most regs die after a C call,
--      but we still don't want to allocate to some of them.
--
releaseReg :: Platform -> RealReg -> FreeRegs -> FreeRegs
releaseReg platform
         reg@(RealRegSingle r)
        regs@(FreeRegs g f d)

        -- don't release pinned reg
        | not $ freeReg platform r
        = regs

        -- a general purpose reg
        | r <= 31
        = let   mask    = bitMask r
          in    FreeRegs (g .|. mask) f d

        -- a float reg
        | r >= 32, r <= 63
        = let   mask    = bitMask (r - 32)

                -- the mask of the double this FP reg aliases
                maskLow = if r `mod` 2 == 0
                                then bitMask (r - 32)
                                else bitMask (r - 32 - 1)
          in    FreeRegs
                        g
                        (f .|. mask)
                        (d .|. maskLow)

        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not releasing bad reg" (ppr reg)

releaseReg _
         reg@(RealRegPair r1 r2)
             (FreeRegs g f d)

        | r1 >= 32, r1 <= 63, r1 `mod` 2 == 0
        , r2 >= 32, r2 <= 63
        = let   mask1   = bitMask (r1 - 32)
                mask2   = bitMask (r2 - 32)
          in
                FreeRegs
                        g
                        ((f .|. mask1) .|. mask2)
                        (d .|. mask1)

        | otherwise
        = pprPanic "SPARC.FreeRegs.releaseReg: not releasing bad reg" (ppr reg)

bitMask :: Int -> Word32
bitMask n       = 1 `shiftL` n

showFreeRegs :: FreeRegs -> String
showFreeRegs regs
        =  "FreeRegs\n"
        ++ "    integer: " ++ (show $ getFreeRegs RcInteger regs)       ++ "\n"
        ++ "      float: " ++ (show $ getFreeRegs RcFloat   regs)       ++ "\n"
        ++ "     double: " ++ (show $ getFreeRegs RcDouble  regs)       ++ "\n"


text:
<!--
    @license
    Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
    This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
    The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
    The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
    Code distributed by Google as part of the polymer project is also
    subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
  <title>seed-element</title>

  <script src="../../webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <script src="../../google-code-prettify/bin/prettify.min.js"></script>

  <link rel="import" href="../code-highlighter.html">
</head>
<body>

  <!-- You use the document as a place to set up your fixtures. -->
  <code-highlighter>
object HelloWorld {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}
  </code-highlighter>

  <script>
    var myEl = document.querySelector('code-highlighter');

    suite('<code-highlighter>', function() {

      test('pretty-prints the code', function() {
        var rawCode = "object HelloWorld {\n" +
                      "  def main(args: Array[String]) {\n" +
                      "    println(\"Hello, world!\")\n" +
                      "  }\n" +
                      "}\n  ";

        assert.equal(myEl.$.code.innerHTML, prettyPrintOne(rawCode));
      });

    });
  </script>

</body>
</html>


text:
. "$(dirname "$0")/testlib.sh"
reponame="submodule-test-repo"
submodname="submodule-test-submodule"

begin_test "submodule local git dir"
(
  set -e

  setup_remote_repo "$reponame"
  setup_remote_repo "$submodname"

  clone_repo "$submodname" submod
  mkdir dir
  echo "sub module" > dir/README
  git add dir/README
  git commit -a -m "submodule readme"
  git push origin main

  clone_repo "$reponame" repo
  git submodule add "$GITSERVER/$submodname" sub
  git submodule update
  git add .gitmodules sub
  git commit -m "add submodule"
  git push origin main

  grep "sub module" sub/dir/README || {
    echo "submodule not setup correctly?"
    cat sub/dir/README
    exit 1
  }
)
end_test

begin_test "submodule env"
(
  set -e

  # using the local clone from the above test
  cd repo

  git lfs env | tee env.log
  grep "Endpoint=$GITSERVER/$reponame.git/info/lfs (auth=none)$" env.log
  grep "LocalWorkingDir=$(canonical_path_escaped "$TRASHDIR/repo$")" env.log
  grep "LocalGitDir=$(canonical_path_escaped "$TRASHDIR/repo/.git$")" env.log
  grep "LocalGitStorageDir=$(canonical_path_escaped "$TRASHDIR/repo/.git$")" env.log
  grep "LocalMediaDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/lfs/objects$")" env.log
  grep "TempDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/lfs/tmp$")" env.log

  cd .git

  echo "./.git"
  git lfs env | tee env.log
  cat env.log
  grep "Endpoint=$GITSERVER/$reponame.git/info/lfs (auth=none)$" env.log
  grep "LocalWorkingDir=$" env.log
  grep "LocalGitDir=$(canonical_path_escaped "$TRASHDIR/repo/.git$")" env.log
  grep "LocalGitStorageDir=$(canonical_path_escaped "$TRASHDIR/repo/.git$")" env.log
  grep "LocalMediaDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/lfs/objects$")" env.log
  grep "TempDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/lfs/tmp$")" env.log

  cd ../sub

  echo "./sub"
  git lfs env | tee env.log
  grep "Endpoint=$GITSERVER/$submodname.git/info/lfs (auth=none)$" env.log
  grep "LocalWorkingDir=$(canonical_path_escaped "$TRASHDIR/repo/sub$")" env.log
  grep "LocalGitDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub$")" env.log
  grep "LocalGitStorageDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub$")" env.log
  grep "LocalMediaDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub/lfs/objects$")" env.log
  grep "TempDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub/lfs/tmp$")" env.log

  cd dir

  echo "./sub/dir"
  git lfs env | tee env.log
  grep "Endpoint=$GITSERVER/$submodname.git/info/lfs (auth=none)$" env.log
  grep "LocalWorkingDir=$(canonical_path_escaped "$TRASHDIR/repo/sub$")" env.log
  grep "LocalGitDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub$")" env.log
  grep "LocalGitStorageDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub$")" env.log
  grep "LocalMediaDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub/lfs/objects$")" env.log
  grep "TempDir=$(canonical_path_escaped "$TRASHDIR/repo/.git/modules/sub/lfs/tmp$")" env.log
)
end_test


text:
<?php
namespace Icecave\Duct\Detail;

use Icecave\Duct\Detail\Exception\LexerException;

/**
 * Streaming JSON lexer.
 *
 * Converts incoming streams of JSON data into tokens.
 *
 * @internal
 */
class Lexer
{
    /**
     * @param string $encoding The encoding of the incoming JSON data stream.
     */
    public function __construct($encoding = 'UTF-8')
    {
        $this->encoding = $encoding;

        $this->reset();
    }

    public function setCallback(callable $callback)
    {
        $this->callback = $callback;
    }

    /**
     * Reset the lexer, discarding any untokened input.
     */
    public function reset()
    {
        $this->state         = LexerState::BEGIN;
        $this->inputBuffer   = '';
        $this->tokenBuffer   = '';
        $this->unicodeBuffer = '';
    }

    /**
     * Feed JSON data to the lexer.
     *
     * @param string $buffer The JSON data.
     *
     * @throws Exception\LexerException
     */
    public function feed($buffer)
    {
        $this->inputBuffer .= $buffer;

        $byteCount = 0;
        $charCount = 0;

        while (true) {
            $char = mb_substr($this->inputBuffer, 0, 1, $this->encoding);

            if ($char === false || $char === '') {
                break;
            }

            $this->inputBuffer = substr($this->inputBuffer, strlen($char));

            if (LexerState::STRING_VALUE === $this->state) {
                $this->doStringValue($char);
            } elseif (LexerState::STRING_VALUE_ESCAPED === $this->state) {
                $this->doStringValueEscaped($char);
            } elseif (LexerState::STRING_VALUE_UNICODE === $this->state) {
                $this->doStringValueUnicode($char);
            } elseif (LexerState::NUMBER_VALUE === $this->state) {
                $this->doNumberValue($char);
            } elseif (LexerState::NUMBER_VALUE_NEGATIVE === $this->state) {
                $this->doNumberValueNegative($char);
            } elseif (LexerState::NUMBER_VALUE_LEADING_ZERO === $this->state) {
                $this->doNumberValueLeadingZero($char);
            } elseif (LexerState::NUMBER_VALUE_DECIMAL === $this->state) {
                $this->doNumberValueDecimal($char);
            } elseif (LexerState::NUMBER_VALUE_EXPONENT_START === $this->state) {
                $this->doNumberValueExponentStart($char);
            } elseif (LexerState::NUMBER_VALUE_EXPONENT === $this->state) {
                $this->doNumberValueExponent($char);
            } elseif (LexerState::TRUE_VALUE === $this->state) {
                $this->doTrueValue($char);
            } elseif (LexerState::FALSE_VALUE === $this->state) {
                $this->doFalseValue($char);
            } elseif (LexerState::NULL_VALUE === $this->state) {
                $this->doNullValue($char);
            } else {
                $this->doBegin($char);
            }
        }
    }

    /**
     * Complete tokenization.
     *
     * @throws Exception\LexerException Indicates that the input terminated midway through a token.
     */
    public function finalize()
    {
        switch ($this->state) {
            case LexerState::NUMBER_VALUE_NEGATIVE:
            case LexerState::NUMBER_VALUE_EXPONENT_START:
            case LexerState::STRING_VALUE:
            case LexerState::STRING_VALUE_ESCAPED:
            case LexerState::STRING_VALUE_UNICODE:
            case LexerState::TRUE_VALUE:
            case LexerState::FALSE_VALUE:
            case LexerState::NULL_VALUE:
                throw new LexerException('Character stream ended while scanning literal value.');

            case LexerState::NUMBER_VALUE:
            case LexerState::NUMBER_VALUE_LEADING_ZERO:
                $this->emitLiteral(TokenType::NUMBER_LITERAL, intval($this->tokenBuffer));
                break;

            case LexerState::NUMBER_VALUE_DECIMAL:
            case LexerState::NUMBER_VALUE_EXPONENT:
                $this->emitLiteral(TokenType::NUMBER_LITERAL, floatval($this->tokenBuffer));
                break;
        }
    }

    /**
     * @param string $char
     */
    private function doBegin($char)
    {
        if ('"' === $char) {
            $this->state = LexerState::STRING_VALUE;
        } elseif ('0' === $char) {
            $this->tokenBuffer = $char;
            $this->state       = LexerState::NUMBER_VALUE_LEADING_ZERO;
        } elseif ('-' === $char) {
            $this->tokenBuffer = $char;
            $this->state       = LexerState::NUMBER_VALUE_NEGATIVE;
        } elseif (ctype_digit($char)) {
            $this->tokenBuffer = $char;
            $this->state       = LexerState::NUMBER_VALUE;
        } elseif ('t' === $char) {
            $this->tokenBuffer = $char;
            $this->state       = LexerState::TRUE_VALUE;
        } elseif ('f' === $char) {
            $this->tokenBuffer = $char;
            $this->state       = LexerState::FALSE_VALUE;
        } elseif ('n' === $char) {
            $this->tokenBuffer = $char;
            $this->state       = LexerState::NULL_VALUE;
        } elseif (false !== strpos('{}[]:,', $char)) {
            $this->emitSpecial($char);
        } elseif (!ctype_space($char)) {
            throw new LexerException('Unexpected character: "' . $char . '".');
        }
    }

    /**
     * @param string $char
     */
    private function doStringValue($char)
    {
        if ('\\' === $char) {
            $this->state = LexerState::STRING_VALUE_ESCAPED;
        } elseif (null !== $this->unicodeHighSurrogate) {
            throw new LexerException('Missing low surrogate for unicode surrogate pair.');
        } elseif ('"' === $char) {
            $this->emitLiteral(TokenType::STRING_LITERAL, $this->tokenBuffer);
        } else {
            $this->tokenBuffer .= $char;
        }
    }

    /**
     * @param string $char
     */
    private function doStringValueEscaped($char)
    {
        if ('u' === $char) {
            $this->unicodeBuffer = '';
            $this->state         = LexerState::STRING_VALUE_UNICODE;
        } elseif (null !== $this->unicodeHighSurrogate) {
            throw new LexerException('Missing low surrogate for unicode surrogate pair.');
        } elseif (array_key_exists($char, self::$escapeSequences)) {
            $this->tokenBuffer .= self::$escapeSequences[$char];
            $this->state = LexerState::STRING_VALUE;
        } else {
            throw new LexerException('Invalid escape sequence.');
        }
    }

    /**
     * @param string $char
     */
    private function doStringValueUnicode($char)
    {
        if (!ctype_xdigit($char)) {
            throw new LexerException('Invalid escape sequence.');
        }

        $this->unicodeBuffer .= $char;

        if (4 === strlen($this->unicodeBuffer)) {
            $codepoint = hexdec($this->unicodeBuffer);

            // Store high surrogate for combination later if is high surrogate...
            if ($codepoint >= 0xd800 && $codepoint <= 0xdbff) {
                if (null !== $this->unicodeHighSurrogate) {
                    throw new LexerException('Multiple high surrogates for unicode surrogate pair.');
                }
                $this->unicodeHighSurrogate = $codepoint;

            // Combine high + low surrogate if is low surrogate ...
            } elseif ($codepoint >= 0xdc00 && $codepoint <= 0xdfff) {
                if (null === $this->unicodeHighSurrogate) {
                    throw new LexerException('Missing high surrogate for unicode surrogate pair.');
                }
                $codepoint = $this->combineUnicodeSurrogateCodepoints($this->unicodeHighSurrogate, $codepoint);
                $this->tokenBuffer .= $this->convertUnicodeCodepoint($codepoint);
                $this->unicodeHighSurrogate = null;

            // Regular (non-surrogate) code-point ...
            } else {
                $this->tokenBuffer .= $this->convertUnicodeCodepoint($codepoint);
            }

            $this->state = LexerState::STRING_VALUE;
        }
    }

    /**
     * @param string $char
     */
    private function doNumberValue($char)
    {
        if (ctype_digit($char)) {
            $this->tokenBuffer .= $char;
        } elseif ('.' === $char) {
            $this->tokenBuffer .= '.';
            $this->state = LexerState::NUMBER_VALUE_DECIMAL;
        } elseif ('e' === $char || 'E' === $char) {
            $this->tokenBuffer .= 'e';
            $this->state = LexerState::NUMBER_VALUE_EXPONENT_START;
        } else {
            $this->emitLiteral(TokenType::NUMBER_LITERAL, intval($this->tokenBuffer));
            $this->doBegin($char);
        }
    }

    /**
     * @param string $char
     */
    private function doNumberValueNegative($char)
    {
        if ('0' === $char) {
            $this->tokenBuffer .= $char;
            $this->state = LexerState::NUMBER_VALUE_LEADING_ZERO;
        } elseif (ctype_digit($char)) {
            $this->tokenBuffer .= $char;
            $this->state = LexerState::NUMBER_VALUE;
        } else {
            throw new LexerException('Expected digit after negative sign.');
        }
    }

    /**
     * @param string $char
     */
    private function doNumberValueLeadingZero($char)
    {
        if ('.' === $char) {
            $this->tokenBuffer .= '.';
            $this->state = LexerState::NUMBER_VALUE_DECIMAL;
        } elseif ('e' === $char || 'E' === $char) {
            $this->tokenBuffer .= 'e';
            $this->state = LexerState::NUMBER_VALUE_EXPONENT_START;
        } else {
            $this->emitLiteral(TokenType::NUMBER_LITERAL, intval($this->tokenBuffer));
            $this->doBegin($char);
        }
    }

    /**
     * @param string $char
     */
    private function doNumberValueDecimal($char)
    {
        if (ctype_digit($char)) {
            $this->tokenBuffer .= $char;
        } elseif ('e' === $char || 'E' === $char) {
            $this->tokenBuffer .= 'e';
            $this->state = LexerState::NUMBER_VALUE_EXPONENT_START;
        } elseif ('.' === substr($this->tokenBuffer, -1)) {
            throw new LexerException('Expected digit after decimal point.');
        } else {
            $this->emitLiteral(TokenType::NUMBER_LITERAL, floatval($this->tokenBuffer));
            $this->doBegin($char);
        }
    }

    /**
     * @param string $char
     */
    private function doNumberValueExponentStart($char)
    {
        if ('+' === $char || '-' === $char) {
            $this->tokenBuffer .= $char;
            $this->state = LexerState::NUMBER_VALUE_EXPONENT;
        } elseif (ctype_digit($char)) {
            $this->tokenBuffer .= $char;
            $this->state = LexerState::NUMBER_VALUE_EXPONENT;
        } else {
            throw new LexerException('Expected digit or +/- as exponent.');
        }
    }

    /**
     * @param string $char
     */
    private function doNumberValueExponent($char)
    {
        if (ctype_digit($char)) {
            $this->tokenBuffer .= $char;
        } else {
            $this->emitLiteral(TokenType::NUMBER_LITERAL, floatval($this->tokenBuffer));
            $this->doBegin($char);
        }
    }

    /**
     * @param string $char
     */
    private function doTrueValue($char)
    {
        $this->tokenBuffer .= $char;

        if ($this->expectString('true')) {
            $this->emitLiteral(TokenType::BOOLEAN_LITERAL, true);
        }
    }

    /**
     * @param string $char
     */
    private function doFalseValue($char)
    {
        $this->tokenBuffer .= $char;

        if ($this->expectString('false')) {
            $this->emitLiteral(TokenType::BOOLEAN_LITERAL, false);
        }
    }

    /**
     * @param string $char
     */
    private function doNullValue($char)
    {
        $this->tokenBuffer .= $char;

        if ($this->expectString('null')) {
            $this->emitLiteral(TokenType::NULL_LITERAL, null);
        }
    }

    /**
     * @param string $char
     */
    private function emitSpecial($char)
    {
        $callback = $this->callback;
        $callback(new Token($char, $char));

        $this->tokenBuffer = '';
        $this->state       = LexerState::BEGIN;
    }

    /**
     * @param integer $type
     * @param string  $value
     */
    private function emitLiteral($type, $value)
    {
        $callback = $this->callback;
        $callback(new Token($type, $value));

        $this->tokenBuffer = '';
        $this->state       = LexerState::BEGIN;
    }

    /**
     * @param string $string
     */
    private function expectString($string)
    {
        if ($this->tokenBuffer === $string) {
            return true;
        } elseif (substr($string, 0, strlen($this->tokenBuffer)) === $this->tokenBuffer) {
            return false;
        }

        throw new LexerException('Expected "' . $string . '", got "' . $this->tokenBuffer . '".');
    }

    /**
     * @param integer $highSurrogate
     * @param integer $lowSurrogate
     */
    private function combineUnicodeSurrogateCodepoints($highSurrogate, $lowSurrogate)
    {
        return 0x10000 + ($highSurrogate - 0xd800) * 0x400 + ($lowSurrogate - 0xdc00);
    }

    /**
     * @param integer $codepoint
     */
    private function convertUnicodeCodepoint($codepoint)
    {
        return mb_convert_encoding(
            sprintf('&#%04d;', $codepoint),
            $this->encoding,
            'HTML-ENTITIES'
        );
    }

    private static $escapeSequences = [
        'b'  => "\x08",
        'f'  => "\f",
        'n'  => "\n",
        'r'  => "\r",
        't'  => "\t",
        '"'  => '"',
        '/'  => '/',
        '\\' => '\\',
    ];

    private $encoding;
    private $callback;
    private $state;
    private $inputBuffer;
    private $tokenBuffer;
    private $unicodeBuffer;
    private $unicodeHighSurrogate;
}


text:
module Fog
  module Compute
    class XenServer
      module Models
        class Network < Model
          # API Reference here:
          # http://docs.vmd.citrix.com/XenServer/6.2.0/1.0/en_gb/api/?c=network

          provider_class :network
          collection_name :networks

          identity :reference

          attribute :allowed_operations
          attribute :blobs
          attribute :bridge
          attribute :current_operations
          attribute :default_locking_mode
          attribute :description,         :aliases => :name_description,  :default => "",  :as => :name_description
          attribute :mtu,                 :aliases => :MTU,                                :as => :MTU
          attribute :name,                :aliases => :name_label,        :default => "",  :as => :name_label
          attribute :other_config,                                        :default => {}
          attribute :tags
          attribute :uuid

          has_many_identities :pifs,  :pifs,         :aliases => :PIFs,   :as => :PIFs
          has_many_identities :vifs,  :vifs,         :aliases => :VIFs,   :as => :VIFs

          require_before_save :name
        end
      end
    end
  end
end


text:
using Grabacr07.KanColleViewer.Composition;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using AMing.Plugins.Core.Extensions;
using Codeplex.Data;

namespace KcvPlugins
{
    /// <summary>
    /// KcvSimulationWindow.xaml 的交互逻辑
    /// </summary>
    public partial class KcvSimulationWindow
    {
        public KcvSimulationWindow()
        {
            //this.AllowsTransparency = true;
            //this.WindowStyle = System.Windows.WindowStyle.None;
            //this.Topmost = true;
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            App.PluginList.ForEach(pugin => AddPlugin(pugin));

            //var msg = AMing.Plugins.Core.Modules.MessagerModules.Current.ToStringContent();

            var text = AMing.Plugins.Core.Helper.TextFileHelper.TxtFileRead(@"C:\Program Files\KanColleViewer\Plugins\Logger\Battle\logs_last.json.txt");

            ////var aaa = new AMing.Logger.Modes.BattleResultList();
            ////var json = AMing.Logger.Helper.JsonHelper.Serialize(aaa);
            //var bbb = AMing.Logger.Helper.JsonHelper.Deserialize<AMing.Logger.Modes.BattleResultList>(text);
            //var json = DynamicJson.Serialize(bbb);
            //var aaa = DynamicJson.Parse(json);
            ////var ccc = new AMing.Logger.Modes.BattleResultList(aaa);
            //var asdasd = (BattleResultList22)aaa;

            //AMing.Logger.Helper.BattleLogsHelper.Current.Append(list =>
            //{
            //    return true;
            //});
            //IList<AMing.Logger.Modes.BattleResult> list;
            //IList<AMing.Logger.Modes.SimpleAdmiral> alist;
            //DateTime lastUpdateDate;
            //AMing.Logger.Helper.BattleLogsHelper.Current.GetInfo(out list, out alist, out lastUpdateDate);

            var item = AMing.Logger.Helper.BattleLogsHelper.Current.GetLastItem();
            //item.Fleet[0].HP_After=123;
            //item.Fleet[1].HP_After = 233;
            //AMing.Logger.Helper.BattleLogsHelper.Current.UpdateItem(item);
        }

        private void AddPlugin(IToolPlugin plugin)
        {
            var tabitem = new TabItem
            {
                Header = plugin.ToolName,
                Content = plugin.GetToolView()
            };

            tabControl.Items.Add(tabitem);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //var temp = AMing.Plugins.Core.Helper.KcvMainWindowControlHelper.Current.StatusBar;

            //var grid = this.statusBar.Content as Grid;
            //var content = grid.Children.OfType<ContentControl>().FirstOrDefault();

            //var result = content.Resources.Cast<DictionaryEntry>().Where((item, i) =>
            //    (item.Key is DataTemplateKey) &&
            //    (item.Key as DataTemplateKey).DataType.Equals(typeof(Grabacr07.KanColleViewer.ViewModels.Contents.Fleets.FleetsViewModel))
            //).FirstOrDefault();
        }
    }
}


text:
<!DOCTYPE html>

  <meta charset="utf-8">
  <base data-ice="baseUrl" href="../../../">
  <title data-ice="title">src/views/Status.js | Smoke and the Water</title>
  <link type="text/css" rel="stylesheet" href="css/style.css">
  <link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">

<meta name="description" content="simple recurrent timer to help you to control how many cigarettes you 🚬 smoke, how many ☕ coffee you drink and help you to remember you to drink pure clean 🚰 water"><meta property="twitter:card" content="summary"><meta property="twitter:title" content="Smoke and the Water"><meta property="twitter:description" content="simple recurrent timer to help you to control how many cigarettes you 🚬 smoke, how many ☕ coffee you drink and help you to remember you to drink pure clean 🚰 water">

  Reference
  Source

    <input class="search-input">

controllersCTimer

helpersCFormat

CTime

modelsCDefaultTimerModel

CRemindeModel

CTimerModel

viewsCAlert

CComputer

CPhone

CStatus

CTerminal

src/views/Status.js

/* eslint-disable no-console */

/**
 * Class for displaying terminal status
 * @class Status
 * @module Status
 */
class Status {
  /**
   * Display terminal log
   * @method show
   * @param  {Mixed} obj Any type of variable
   */
  static log(obj) {
    console.log(obj);
  }
  /**
   * Display terminal error
   * @method error
   * @param  {Mixed} obj Any type of variable
   */
  static error(obj) {
    console.error(obj);
  }
}

module.exports = Status;

  Generated by ESDoc(1.0.1)


text:
describe('Service: $cordovaSpinnerDialog', function() {

  var $cordovaSpinnerDialog;

  beforeEach(module('ngCordova.plugins.spinnerDialog'));

  beforeEach(inject(function (_$cordovaSpinnerDialog_) {
    $cordovaSpinnerDialog = _$cordovaSpinnerDialog_;

    window.plugins = {
      spinnerDialog: jasmine.createSpyObj('spinnerDialog', ['hide', 'show'])
    }
  }));

  it('should call window.plugins\'s spinnerDialog.show method with options', function() {
    $cordovaSpinnerDialog.show('hi', 'nice hi message', true, {overlayOpacity: 0.75,  textColorRed: 1, textColorGreen: 0, textColorBlue: 0.4});
    expect(window.plugins.spinnerDialog.show).toHaveBeenCalledWith('hi', 'nice hi message', true, {overlayOpacity: 0.75,  textColorRed: 1, textColorGreen: 0, textColorBlue: 0.4});
  });

  it('should call window.plugins\'s spinnerDialog.show method without options', function() {
    $cordovaSpinnerDialog.show('hi', 'nice hi message', true);
    expect(window.plugins.spinnerDialog.show).toHaveBeenCalledWith('hi', 'nice hi message', true, undefined);
  });

  it('should call window.plugins\'s spinnerDialog.hide', function() {
    $cordovaSpinnerDialog.hide();
    expect(window.plugins.spinnerDialog.hide).toHaveBeenCalled();
  });

});


text:
  #include <stdio.h>
  #include <time.h>
  #include <string.h>
  #include <sys/time.h>
  #include <stddef.h>

  // Additional fixes for LittleWire
  #include <LittleWireSPI/littlewirespi.h>
  #include <LittleWireSPI/avr_fixes.h>
  extern LittleWireSPI _SPI;

  // GCC a Arduino Missing
  #define _BV(x) (1<<(x))
  #define pgm_read_word(p) (*(p))
  #define pgm_read_byte(p) (*(p))

  //typedef uint16_t prog_uint16_t;
  #define PSTR(x) (x)
  #define printf_P printf
  #define strlen_P strlen
  #define PROGMEM
  #define PRIPSTR "%s"

  #ifdef SERIAL_DEBUG
	#define IF_SERIAL_DEBUG(x) ({x;})
  #else
	#define IF_SERIAL_DEBUG(x)
	#if defined(RF24_TINY)
	  #define printf_P(...)
    #endif
  #endif

#endif


text:
    :

          Add


text:
from ..player import Player
from ...utils import check_random_state

class RandPlayer(Player):

    name = 'RandPlayer'

    def __init__(self, random_state=None):
        self.random_state = check_random_state(random_state)

    def __str__(self):
        return 'RandPlayer'

    def __repr__(self):
        return 'RandPlayer'

    ##########
    # Player #
    ##########

    def choose_move(self, game):
        return self.random_state.choice(game.legal_moves())