Cara Kerja Konten Interaktif BBC di AMP, Aplikasi, dan Web

Diterbitkan: 2022-03-10
Ringkasan cepat Menerbitkan konten ke begitu banyak media tanpa banyak biaya pengembangan tambahan bisa jadi sulit. Chris Ashton menjelaskan bagaimana mereka mendekati masalah di departemen Jurnalisme Visual BBC.

Di tim Jurnalisme Visual di BBC, kami memproduksi konten visual, menarik, dan interaktif yang menarik, mulai dari kalkulator hingga visualisasi format bercerita baru.

Setiap aplikasi merupakan tantangan unik untuk diproduksi dengan caranya sendiri, tetapi terlebih lagi jika Anda menganggap bahwa kami harus menerapkan sebagian besar proyek dalam berbagai bahasa. Konten kami harus berfungsi tidak hanya di situs web Berita dan Olahraga BBC tetapi juga pada aplikasi yang setara di iOS dan Android, serta di situs pihak ketiga yang menggunakan konten BBC.

Sekarang pertimbangkan bahwa ada semakin banyak platform baru seperti AMP, Artikel Instan Facebook, dan Apple News. Setiap platform memiliki batasan dan mekanisme penerbitan eksklusifnya sendiri. Membuat konten interaktif yang berfungsi di semua lingkungan ini adalah tantangan nyata. Saya akan menjelaskan bagaimana kami mendekati masalah di BBC.

Contoh: Canonical vs. AMP

Ini semua sedikit teoretis sampai Anda melihatnya beraksi, jadi mari kita langsung ke sebuah contoh.

Berikut adalah artikel BBC yang berisi konten Jurnalisme Visual:

Tangkapan layar halaman BBC News yang berisi konten Jurnalisme Visual
Konten Jurnalisme Visual kami dimulai dengan ilustrasi Donald Trump, dan berada di dalam iframe

Ini adalah versi kanonik artikel, yaitu versi default, yang akan Anda dapatkan jika membuka artikel dari beranda.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Sekarang mari kita lihat artikel versi AMP:

Tangkapan layar halaman AMP Berita BBC yang berisi konten yang sama seperti sebelumnya, tetapi konten terpotong dan memiliki tombol Tampilkan Lainnya
Ini terlihat seperti konten yang sama dengan artikel biasa, tetapi menggunakan iframe berbeda yang dirancang khusus untuk AMP

Meskipun versi kanonis dan AMP terlihat sama, keduanya sebenarnya adalah dua titik akhir yang berbeda dengan perilaku yang berbeda:

  • Versi kanonik menggulir Anda ke negara pilihan Anda saat Anda mengirimkan formulir.
  • Versi AMP tidak men-scroll Anda, karena Anda tidak dapat men-scroll halaman induk dari dalam iframe AMP.
  • Versi AMP menampilkan iframe yang dipangkas dengan tombol 'Tampilkan Lainnya', bergantung pada ukuran area pandang dan posisi gulir. Ini adalah fitur dari AMP.

Selain versi kanonik dan AMP dari artikel ini, proyek ini juga dikirimkan ke Aplikasi Berita, yang merupakan platform lain dengan kerumitan dan keterbatasannya sendiri. Jadi bagaimana kami mendukung semua platform ini?

Perkakas Adalah Kunci

Kami tidak membangun konten kami dari awal. Kami memiliki perancah berbasis Yeoman yang menggunakan Node untuk menghasilkan proyek boilerplate dengan satu perintah.

Proyek baru hadir dengan Webpack, SASS, penerapan, dan struktur komponenisasi di luar kotak. Internasionalisasi juga dimasukkan ke dalam proyek kami, menggunakan sistem templating Handlebars. Tom Maslen menulis tentang ini secara rinci dalam postingnya, 13 tips untuk membuat desain web responsif multi-bahasa.

Di luar kotak, ini berfungsi cukup baik untuk kompilasi untuk satu platform tetapi kami perlu mendukung banyak platform . Mari kita mempelajari beberapa kode.

Sematkan vs. Mandiri

Dalam Jurnalisme Visual, kami terkadang menampilkan konten kami di dalam iframe sehingga dapat menjadi "sematan" mandiri dalam sebuah artikel, tidak terpengaruh oleh skrip dan gaya global. Contohnya adalah interaktif Donald Trump yang disematkan dalam contoh kanonik sebelumnya di artikel ini.

Di sisi lain, terkadang kami menampilkan konten kami sebagai HTML mentah. Kami hanya melakukan ini ketika kami memiliki kendali atas seluruh halaman atau jika kami memerlukan interaksi gulir yang sangat responsif. Mari kita sebut ini sebagai output "sematkan" dan "mandiri".

Mari kita bayangkan bagaimana kita bisa membangun "Akankah robot mengambil pekerjaan Anda?" interaktif dalam format "sematkan" dan "mandiri".

Dua tangkapan layar berdampingan. Satu menunjukkan konten yang disematkan di halaman; yang lain menunjukkan konten yang sama sebagai halaman dalam dirinya sendiri.
Contoh yang dibuat-buat yang menunjukkan 'sematan' di sebelah kiri, versus konten sebagai halaman 'mandiri' di sebelah kanan

Kedua versi konten akan berbagi sebagian besar kode mereka, tetapi akan ada beberapa perbedaan penting dalam penerapan JavaScript antara kedua versi.

Misalnya, lihat tombol 'Cari tahu risiko otomatisasi saya'. Ketika pengguna menekan tombol kirim, mereka harus secara otomatis menggulir ke hasil mereka.

Versi kode "mandiri" mungkin terlihat seperti ini:

 button.on('click', (e) => { window.scrollTo(0, resultsContainer.offsetTop); });

Tetapi jika Anda membangun ini sebagai output "sematkan", Anda tahu bahwa konten Anda ada di dalam iframe, jadi perlu mengkodekannya secara berbeda:

 // inside the iframe button.on('click', () => { window.parent.postMessage({ name: 'scroll', offset: resultsContainer.offsetTop }, '*'); }); // inside the host page window.addEventListener('message', (event) => { if (event.data.name === 'scroll') { window.scrollTo(0, iframe.offsetTop + event.data.offset); } });

Juga, bagaimana jika aplikasi kita perlu layar penuh? Ini cukup mudah jika Anda berada di halaman "mandiri":

 document.body.className += ' fullscreen';
 .fullscreen { position: fixed; top: 0; left: 0; right: 0; bottom: 0; } 
Tangkapan layar peta yang disematkan dengan hamparan 'Ketuk untuk Berinteraksi', diikuti dengan tangkapan layar peta dalam mode layar penuh setelah diketuk.
Kami berhasil menggunakan fungsionalitas layar penuh untuk memaksimalkan modul peta kami di ponsel

Jika kami mencoba melakukan ini dari dalam "sematkan", kode yang sama ini akan memiliki penskalaan konten ke lebar dan tinggi iframe , daripada viewport:

Tangkapan layar contoh peta seperti sebelumnya, tetapi mode layar penuh bermasalah. Teks dari artikel di sekitarnya terlihat di tempat yang tidak seharusnya.
Mungkin sulit untuk beralih ke layar penuh dari dalam iframe

…jadi selain menerapkan gaya layar penuh di dalam iframe, kita harus mengirim pesan ke halaman host untuk menerapkan gaya ke iframe itu sendiri:

 // iframe window.parent.postMessage({ name: 'window:toggleFullScreen' }, '*'); // host page window.addEventListener('message', function () { if (event.data.name === 'window:toggleFullScreen') { document.getElementById(iframeUid).className += ' fullscreen'; } });

Ini dapat diterjemahkan ke dalam banyak kode spageti ketika Anda mulai mendukung banyak platform:

 button.on('click', (e) => { if (inStandalonePage()) { window.scrollTo(0, resultsContainer.offsetTop); } else { window.parent.postMessage({ name: 'scroll', offset: resultsContainer.offsetTop }, '*'); } });

Bayangkan melakukan hal yang setara dengan ini untuk setiap interaksi DOM yang berarti dalam proyek Anda. Setelah Anda selesai bergidik, buatkan diri Anda secangkir teh yang menenangkan, dan baca terus.

Abstraksi Adalah Kunci

Daripada memaksa pengembang kami untuk menangani persyaratan ini di dalam kode mereka, kami membangun lapisan abstraksi antara konten mereka dan lingkungan. Kami menyebut lapisan ini sebagai 'pembungkus'.

Alih-alih menanyakan DOM atau kejadian browser asli secara langsung, sekarang kita dapat mem-proksi permintaan kita melalui modul wrapper .

 import wrapper from 'wrapper'; button.on('click', () => { wrapper.scrollTo(resultsContainer.offsetTop); });

Setiap platform memiliki implementasi pembungkusnya sendiri yang sesuai dengan antarmuka umum metode pembungkus. Pembungkus membungkus dirinya sendiri di sekitar konten kita dan menangani kerumitan untuk kita.

Diagram UML menunjukkan bahwa ketika aplikasi kita memanggil metode scroll wrapper mandiri, wrapper memanggil metode scroll asli di halaman host.
Implementasi 'scrollTo' sederhana oleh pembungkus mandiri

Implementasi fungsi scrollTo pembungkus mandiri sangat sederhana, meneruskan argumen kita langsung ke window.scrollTo di bawah tenda.

Sekarang mari kita lihat pembungkus terpisah yang mengimplementasikan fungsi yang sama untuk iframe:

Diagram UML menunjukkan bahwa saat aplikasi kita memanggil metode scroll wrapper embed, embed wrapper menggabungkan posisi scroll yang diminta dengan offset iframe sebelum memicu metode scroll native di halaman host.
Implementasi 'scrollTo' lanjutan oleh pembungkus sematan

Pembungkus "sematkan" mengambil argumen yang sama seperti pada contoh "mandiri" tetapi memanipulasi nilai sehingga offset iframe diperhitungkan. Tanpa penambahan ini, kami akan menggulir pengguna kami di suatu tempat yang sama sekali tidak diinginkan.

Pola Pembungkus

Menggunakan pembungkus menghasilkan kode yang lebih bersih, lebih mudah dibaca, dan konsisten antar proyek. Ini juga memungkinkan optimasi mikro dari waktu ke waktu, karena kami melakukan peningkatan bertahap pada pembungkus untuk membuat metode mereka lebih berperforma dan dapat diakses. Oleh karena itu, proyek Anda dapat memperoleh manfaat dari pengalaman banyak pengembang.

Jadi, seperti apa pembungkus itu?

Struktur Pembungkus

Setiap wrapper pada dasarnya terdiri dari tiga hal: template Handlebars, file wrapper JS, dan file SASS yang menunjukkan gaya khusus wrapper. Selain itu, ada tugas pembangunan yang menghubungkan ke peristiwa yang diekspos oleh perancah yang mendasarinya sehingga setiap pembungkus bertanggung jawab atas pra-kompilasi dan pembersihannya sendiri.

Ini adalah tampilan yang disederhanakan dari pembungkus sematan:

 embed-wrapper/ templates/ wrapper.hbs js/ wrapper.js scss/ wrapper.scss

Scaffolding dasar kami memaparkan template proyek utama Anda sebagai bagian Handlebars, yang digunakan oleh wrapper. Misalnya, templates/wrapper.hbs mungkin berisi:

 <div class="bbc-news-vj-wrapper--embed"> {{>your-application}} </div>

scss/wrapper.scss berisi gaya khusus pembungkus yang tidak perlu didefinisikan oleh kode aplikasi Anda sendiri. Pembungkus embed, misalnya, mereplikasi banyak gaya BBC News di dalam iframe.

Terakhir, js/wrapper.js berisi implementasi iframe dari wrapper API, yang dirinci di bawah ini. Itu dikirimkan secara terpisah ke proyek, daripada dikompilasi dengan kode aplikasi — kami menandai wrapper sebagai global dalam proses pembuatan Webpack kami. Ini berarti bahwa meskipun kami mengirimkan aplikasi kami ke berbagai platform, kami hanya mengkompilasi kode satu kali.

API Pembungkus

API pembungkus mengabstraksi sejumlah interaksi browser utama. Berikut adalah yang paling penting:

scrollTo(int)

Gulir ke posisi yang diberikan di jendela aktif. Pembungkus akan menormalkan bilangan bulat yang disediakan sebelum memicu gulir sehingga halaman host digulir ke posisi yang benar.

getScrollPosition: int

Mengembalikan posisi gulir pengguna saat ini (dinormalisasi). Dalam kasus iframe, ini berarti bahwa posisi gulir yang diteruskan ke aplikasi Anda sebenarnya negatif hingga iframe berada di bagian atas viewport. Ini sangat berguna dan memungkinkan kita melakukan hal-hal seperti menganimasikan suatu komponen hanya ketika komponen itu terlihat.

onScroll(callback)

Menyediakan kait ke acara gulir. Dalam pembungkus mandiri, ini pada dasarnya menghubungkan ke acara gulir asli. Di pembungkus sematan, akan ada sedikit penundaan dalam menerima acara gulir karena diteruskan melalui postMessage.

viewport: {height: int, width: int}

Sebuah metode untuk mengambil tinggi dan lebar viewport (karena ini diterapkan sangat berbeda ketika ditanya dari dalam iframe).

toggleFullScreen

Dalam mode mandiri, kami menyembunyikan menu dan footer BBC dari tampilan dan mengatur position: fixed pada konten kami. Di Aplikasi Berita, kami tidak melakukan apa pun — konten sudah dalam layar penuh. Yang rumit adalah iframe, yang bergantung pada penerapan gaya baik di dalam maupun di luar iframe, yang dikoordinasikan melalui postMessage.

markPageAsLoaded

Beri tahu pembungkus konten Anda telah dimuat. Ini penting agar konten kami berfungsi di Aplikasi Berita, yang tidak akan berusaha menampilkan konten kami kepada pengguna sampai kami secara eksplisit memberi tahu aplikasi bahwa konten kami sudah siap. Itu juga menghapus pemintal pemuatan di versi web konten kami.

Daftar Pembungkus

Di masa mendatang, kami membayangkan membuat pembungkus tambahan untuk platform besar seperti Artikel Instan Facebook dan Apple News. Kami telah membuat enam pembungkus hingga saat ini:

Pembungkus Mandiri

Versi konten kami yang seharusnya masuk ke halaman mandiri. Dilengkapi dengan branding BBC.

Sematkan Pembungkus

Versi iframe dari konten kami, yang aman untuk ditempatkan di dalam artikel atau untuk disindikasikan ke situs non-BBC, karena kami memegang kendali atas konten tersebut.

Pembungkus AMP

Ini adalah titik akhir yang ditarik sebagai amp-iframe ke halaman AMP.

Pembungkus Aplikasi Berita

Konten kami harus melakukan panggilan ke protokol bbcvisualjournalism:// milik.

Pembungkus Inti

Hanya berisi HTML — tidak ada CSS atau JavaScript proyek kami.

Pembungkus JSON

Representasi JSON dari konten kami, untuk dibagikan di seluruh produk BBC.

Pembungkus Kabel Hingga Platform

Agar konten kami muncul di situs BBC, kami menyediakan jalur dengan spasi nama kepada jurnalis:

 /include/[department]/[unique ID], eg /include/visual-journalism/123-quiz

Wartawan menempatkan "include path" ini ke dalam CMS, yang menyimpan struktur artikel ke dalam database. Semua produk dan layanan berada di hilir mekanisme penerbitan ini. Setiap platform bertanggung jawab untuk memilih jenis konten yang diinginkan dan meminta konten tersebut dari server proxy.

Mari kita ambil interaktif Donald Trump dari sebelumnya. Di sini, jalur sertakan dalam CMS adalah:

 /include/newsspec/15996-trump-tracker/english/index

Halaman artikel kanonik mengetahui bahwa ia menginginkan versi konten "sematkan", sehingga menambahkan /embed ke jalur penyertaan:

 /include/newsspec/15996-trump-tracker/english/index /embed

…sebelum memintanya dari server proxy:

 https://news.files.bbci.co.uk/include/newsspec/15996-trump-tracker/english/index/embed

Halaman AMP, di sisi lain, melihat jalur include dan menambahkan /amp :

 /include/newsspec/15996-trump-tracker/english/index /amp

Perender AMP melakukan sedikit keajaiban untuk merender beberapa HTML AMP yang mereferensikan konten kita, menarik versi /amp sebagai iframe:

 <amp-iframe src="https://news.files.bbci.co.uk/include/newsspec/15996-trump-tracker/english/index/amp" width="640" height="360"> <!-- some other AMP elements here --> </amp-iframe>

Setiap platform yang didukung memiliki versi kontennya sendiri:

 /include/newsspec/15996-trump-tracker/english/index /amp

/include/newsspec/15996-trump-tracker/english/index /core

/include/newsspec/15996-trump-tracker/english/index /envelope

...dan seterusnya

Solusi ini dapat diskalakan untuk memasukkan lebih banyak jenis platform saat muncul.

Abstraksi Itu Sulit

Membangun arsitektur "tulis sekali, sebarkan di mana saja" terdengar cukup idealis, dan memang demikian. Agar arsitektur pembungkus berfungsi, kita harus sangat ketat dalam bekerja di dalam abstraksi. Ini berarti kita harus melawan godaan untuk “melakukan peretasan ini untuk membuatnya bekerja di [masukkan nama platform di sini].” Kami ingin konten kami sama sekali tidak menyadari lingkungan pengirimannya — tetapi ini lebih mudah diucapkan daripada dilakukan.

Fitur Platform Sulit Dikonfigurasi Secara Abstrak

Sebelum pendekatan abstraksi kami, kami memiliki kontrol penuh atas setiap aspek keluaran kami, termasuk, misalnya, markup iframe kami. Jika kami perlu mengubah apa pun berdasarkan proyek, seperti menambahkan atribut title ke iframe untuk alasan aksesibilitas, kami hanya dapat mengedit markup.

Sekarang markup pembungkus ada dalam isolasi dari proyek, satu-satunya cara untuk mengonfigurasinya adalah dengan mengekspos kait di perancah itu sendiri. Kita dapat melakukan ini dengan relatif mudah untuk fitur lintas platform, tetapi mengekspos kait untuk platform tertentu merusak abstraksi. Kami tidak benar-benar ingin mengekspos opsi konfigurasi 'judul iframe' yang hanya digunakan oleh satu pembungkus.

Kita bisa menamai properti secara lebih umum, misalnya title , dan kemudian menggunakan nilai ini sebagai atribut title iframe. Namun, mulai menjadi sulit untuk melacak apa yang digunakan di mana, dan kami berisiko mengabstraksi konfigurasi kami hingga tidak lagi memahaminya. Pada umumnya, kami mencoba untuk menjaga konfigurasi kami tetap ramping mungkin, hanya mengatur properti yang memiliki penggunaan global.

Perilaku Komponen Bisa Kompleks

Di web, modul alat berbagi kami menampilkan tombol berbagi jaringan sosial yang dapat diklik satu per satu dan membuka pesan berbagi yang telah diisi sebelumnya di jendela baru.

Tangkapan layar bagian alat berbagi BBC berisi ikon media sosial Twitter dan Facebook.
Alat berbagi Jurnalisme Visual BBC menyajikan daftar opsi berbagi sosial

Di Aplikasi Berita, kami tidak ingin berbagi melalui web seluler. Jika pengguna telah menginstal aplikasi yang relevan (misalnya Twitter), kami ingin berbagi di aplikasi itu sendiri. Idealnya, kami ingin menyajikan menu berbagi iOS/Android asli kepada pengguna, lalu membiarkan mereka memilih opsi berbagi sebelum kami membuka aplikasi untuk mereka dengan pesan berbagi yang sudah diisi sebelumnya. Kami dapat memicu menu berbagi asli dari aplikasi dengan melakukan panggilan ke protokol bbcvisualjournalism:// milik.

Cuplikan layar menu berbagi di Android dengan opsi untuk berbagi melalui Pesan, Bluetooth, Salin ke papan klip, dan sebagainya.
Menu berbagi asli di Android

Namun, layar ini akan dipicu apakah Anda mengetuk 'Twitter' atau 'Facebook' di bagian 'Bagikan hasil Anda', sehingga pengguna akhirnya harus membuat pilihan dua kali; pertama kali di dalam konten kami, dan kedua kalinya di popup asli.

Ini adalah perjalanan pengguna yang aneh, jadi kami ingin menghapus ikon berbagi individu dari aplikasi Berita dan menampilkan tombol berbagi umum sebagai gantinya. Kami dapat melakukan ini dengan secara eksplisit memeriksa pembungkus mana yang digunakan sebelum kami merender komponen.

Tangkapan layar tombol bagikan aplikasi berita. Ini adalah satu tombol dengan teks berikut: 'Bagikan bagaimana Anda melakukannya'.
Tombol berbagi umum yang digunakan di Aplikasi Berita

Membangun lapisan abstraksi pembungkus bekerja dengan baik untuk proyek secara keseluruhan, tetapi ketika pilihan pembungkus Anda memengaruhi perubahan pada tingkat komponen , sangat sulit untuk mempertahankan abstraksi yang bersih. Dalam hal ini, kami kehilangan sedikit abstraksi, dan kami memiliki beberapa logika forking yang berantakan dalam kode kami. Untungnya, kasus-kasus ini sedikit dan jarang terjadi.

Bagaimana Kami Menangani Fitur yang Hilang?

Menjaga abstraksi semuanya baik dan bagus. Kode kami memberi tahu wrapper apa yang diinginkan platform untuk dilakukan, misalnya "layar penuh". Tetapi bagaimana jika platform tujuan pengiriman kami tidak dapat benar-benar menjadi layar penuh?

Pembungkus akan mencoba yang terbaik untuk tidak pecah sama sekali, tetapi pada akhirnya Anda memerlukan desain yang dengan anggun kembali ke solusi yang berfungsi apakah metode ini berhasil atau tidak. Kami harus mendesain secara defensif.

Katakanlah kita memiliki bagian hasil yang berisi beberapa diagram batang. Kami sering ingin mempertahankan nilai diagram batang pada nol hingga diagram digulir ke tampilan, di mana kami memicu batang yang bergerak ke lebar yang benar.

Tangkapan layar kumpulan diagram batang yang membandingkan area pengguna dengan rata-rata nasional. Setiap bilah memiliki nilainya yang ditampilkan sebagai teks di sebelah kanan bilah.
Diagram batang menunjukkan nilai yang relevan dengan area saya

Tetapi jika kita tidak memiliki mekanisme untuk menghubungkan ke posisi scroll — seperti yang terjadi pada pembungkus AMP kita — maka bilah akan selamanya tetap nol, yang merupakan pengalaman yang benar-benar menyesatkan.

Tangkapan layar diagram batang yang sama seperti sebelumnya, tetapi batang memiliki 0&#37; lebar dan nilai setiap batang ditetapkan pada 0&#37;. Ini tidak benar.
Bagaimana tampilan diagram batang jika acara pengguliran tidak diteruskan

Kami semakin mencoba untuk mengadopsi lebih banyak pendekatan peningkatan progresif dalam desain kami. Misalnya, kami dapat menyediakan tombol yang akan terlihat untuk semua platform secara default, tetapi akan disembunyikan jika pembungkusnya mendukung pengguliran. Dengan begitu, jika scroll gagal memicu animasi, pengguna masih dapat memicu animasi secara manual.

Tangkapan layar diagram batang yang sama dengan 0&#37; diagram batang, tetapi kali ini dengan hamparan abu-abu halus dan tombol di tengah yang mengundang pengguna untuk 'Melihat hasil'.
Kita bisa menampilkan tombol mundur sebagai gantinya, yang memicu animasi saat diklik.

Rencana Untuk Masa Depan

Kami berharap dapat mengembangkan pembungkus baru untuk platform seperti Apple News dan Artikel Instan Facebook, serta menawarkan semua platform baru versi 'inti' dari konten kami di luar kotak.

Kami juga berharap untuk menjadi lebih baik dalam peningkatan progresif; berhasil di bidang ini berarti berkembang secara defensif. Anda tidak akan pernah bisa berasumsi bahwa semua platform sekarang dan di masa depan akan mendukung interaksi tertentu, tetapi proyek yang dirancang dengan baik harus dapat menyampaikan pesan intinya tanpa jatuh pada rintangan teknis pertama.

Bekerja dalam batas-batas pembungkus adalah sedikit perubahan paradigma, dan terasa seperti rumah setengah jalan dalam hal solusi jangka panjang . Tetapi sampai industri matang menjadi standar lintas platform, penerbit akan dipaksa untuk meluncurkan solusi mereka sendiri, atau menggunakan alat seperti Distro untuk konversi platform-ke-platform, atau mengabaikan seluruh bagian audiens mereka sama sekali.

Ini adalah hari-hari awal bagi kami, tetapi sejauh ini kami telah sukses besar dalam menggunakan pola pembungkus untuk membangun konten kami sekali dan mengirimkannya ke berbagai platform yang sekarang digunakan audiens kami.